LINUX.ORG.RU

Создать дескриптор недоступный для записи

 


1

2

Как создать дескриптор, натравливая на который select(), select() будет сообщать, что дескриптор не готов для записи? Можно конечно создать неблокирующий сокет и сделать connect() на какой-нибудь хост и порт куда коннект не идёт (google.com:81), но это немного костыльно. Может есть какое-то устройство в /dev/ с таким свойством? Хотя неплохо бы какое-то портабельное решение nix/windows.

По-моему, сам факт создания завдедомо незаписываемого дескриптора является костылём. Опишите пожалуйста задачу в более общих чертах.

дескриптор
портабельное решение

Ржал всем офисом ©

Northsoft ★★
()

Для сокета можно закрыть чтение или запись так:

shutdown( descr, SHUT_WR );   // запись
shutdown( descr, SHUT_RD );   // чтение
shutdown( descr, SHUT_RDWR ); // чтение и запись

grondek
()
Ответ на: комментарий от Deleted

Тогда /dev/null, /dev/zero как раз для раздачи нулей

Stil ★★★★★
()
Ответ на: комментарий от grondek

Нет, это не срабатывает. select() всё равно говорит, что сокет доступен для записи.

Olegymous ★★★
() автор топика
Ответ на: комментарий от mky

О сколько нам открытий чудных...

На FreeBSD и OS X такой дескриптор от пайпа помечается как доступный для записи %)

Olegymous ★★★
() автор топика
Ответ на: О сколько нам открытий чудных... от Olegymous

Ну можно попробовать забить pipe, у него должен быть буфер конечного размера (http://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer). При этом в программе нужно предусмотреть, что если select() вдруг скажет, что в pipe возможна запись, то забивать его по одному байту. Только нужно ограничить в программе максимальный объём, записываемый в pipe, а то ещё на какой платформе будет всё ОЗУ съедать :-).

Костыль тот ещё, но, ИМХО, более надёжный, чем (google.com:81), вдруг где-нибудь все порты будут завернуты на прозрачный прокси или ещё куда.

mky ★★★★★
()
Ответ на: комментарий от mky

Кстати да, рабочий вариант. Только это уже с pipefd[1] нужно работать. Единственное смущает 65_000 итераций write() и select() для забития по одному байту. Я так понял в среднем на системах буфер примерно 65 кб.

Есть еще вот этот вариант про запас: http://stackoverflow.com/a/904609
Интересно, он может где-то несработать?

Olegymous ★★★
() автор топика
Последнее исправление: Olegymous (всего исправлений: 1)
Ответ на: комментарий от Olegymous

А, собственно, чтоб не делать 65_000 итераций можно перевести дескриптор в неблокирующий режим и писать туда большими порциями пока EWOULDBLOCK не настанет. Так что этот способ не такой уж и плохой.

Olegymous ★★★
() автор топика
Ответ на: комментарий от Olegymous

Я как понял из той ссылке, что приводил, что на OS X адаптивный алгоритм, поэтому при записи в pipe небольшими объёмами буфер будет 16 кбайт, а большими 65 кбайт.

Работать, ИМХО, нужно с pipefd[0], тогда под Линуксом вобще не будет расход лишней памяти, а под BSD и OS X буфер пайпа.

ИМХО, чем меньше памяти уйдёт в пустую, тем лучше, может потом это полукостыльное решение перейдёт в многопотную систему и когда каждый поток «съест» 65 кбайт, то этого объема ОЗУ будет жалко.

Относительно коннекта на несуществующий адрес нужно изучать отдельно. Ну может быть в ядре вобще нет поддержки сети, нет сискола connect() (хотя где сейчас такое). Нужно смотреть, на какие состояния tcp-содениния select() возвращает готовность к записи. И смотреть, не может ли такой сокет на несуществующий адрес менять свои состояния в течении времени в зависимости, от настроек ядра типа addr-reuse, keep-alive.

А так, всё, что связано с сетью, подвержено влиянию фаервола, поэтому тут лучше закладывать в документации к программе явные требования к правилам фаервола. Ну и проблема в том, что эти правила могут быть изменены админам когда программа уже работает.

Почему в вашей ссылке фигурирует 10.255.255.1 я не совсем понял. Вполне используемый адрес в крупных сетях предприятий.

Вобще, то что select() на сокет на несуществующий адрес возвращает неготовность для записи, вроде как, не оговорено стандартом. ИМХО, если select() вернёт готовность для записи, а write() вернёт ошибку EBADF или EINVAL, то требования стандарта выполняются, так как write() не заблокировался на дескрипторе после select(). То есть сегодня это так, а завтра реализацию select() могут изменить. А с pipe'ом изменять существующее поведение нельзя, там, если write() вернёт ошибку при записи в забитый pipe, много каких программ перестанут работать.

mky ★★★★★
()
Ответ на: комментарий от mky

Я как понял из той ссылке, что приводил, что на OS X адаптивный алгоритм, поэтому при записи в pipe небольшими объёмами буфер будет 16 кбайт, а большими 65 кбайт.

Работать, ИМХО, нужно с pipefd[0], тогда под Линуксом вобще не будет расход лишней памяти, а под BSD и OS X буфер пайпа.

ИМХО, чем меньше памяти уйдёт в пустую, тем лучше, может потом это полукостыльное решение перейдёт в многопотную систему и когда каждый поток «съест» 65 кбайт, то этого объема ОЗУ будет жалко.

Ну я так и сделал. Под нормальными системами, где select() говорит, что pipefd[0] не готов для записи его и возвращаем. А в альтернативно развитых берём pipefd[1], забиваем буфер и возвращаем его. В pipefd[0] забить буфер не получится потому что это дескриптор для чтения по сути и при попытке записи туда возвращается ошибка EBADF. Я выше кидал ссылку на реализацию.

Olegymous ★★★
() автор топика
Последнее исправление: Olegymous (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.