Особенности поведения вызовов read() и write() для pip'а
Системные вызовы read() и write() имеют определенные особенности поведения при работе с pip’ом, связанные с его ограниченным размером, задержками в передаче данных и возможностью блокирования обменивающихся информацией процессов. Организация запрета блокирования этих вызовов для pipe выходит за рамки нашего курса.
Будьте внимательны при написании программ, обменивающихся большими объемами информации через pipe. Помните, что за один раз из pip’а может прочитаться меньше информации, чем вы запрашивали, и за один раз в pipe может записаться меньше информации, чем вам хотелось бы. Проверяйте значения, возвращаемые вызовами!
Одна из особенностей поведения блокирующегося системного вызова read() связана с попыткой чтения из пустого pip'а. Если есть процессы, у которых этот pipe открыт для записи, то системный вызов блокируется и ждет появления информации. Если таких процессов нет, он вернет значение 0 без блокировки процесса. Эта особенность приводит к необходимости закрытия файлового дескриптора, ассоциированного с входным концом pip'a, в процессе, который будет использовать pipe для чтения (close(fd[1]) в процессе-ребенке в программе из раздела "Прогон программы для организации однонаправленной связи между родственными процессами через pipe"). Аналогичной особенностью поведения при отсутствии процессов, у которых pipe открыт для чтения, обладает и системный вызов write(), с чем связана необходимость закрытия файлового дескриптора, ассоциированного с выходным концом pip'a, в процессе, который будет использовать pipe для записи (close(fd[0]) в процессе-родителе в той же программе).
Системные вызовы read и write (продолжение) Особенности поведения при работе с pipe, FIFO и socket Системный вызов read | ||
Попытка прочитать меньше байт, чем есть в наличии в канале связи. | Читает требуемое количество байт и возвращает значение, соответствующее прочитанному количеству. Прочитанная информация удаляется из канала связи. | |
В канале связи находится меньше байт, чем затребовано, но не нулевое количество. | Читает все, что есть в канале связи, и возвращает значение, соответствующее прочитанному количеству. Прочитанная информация удаляется из канала связи. | |
Попытка читать из канала связи, в котором нет информации. Блокировка вызова разрешена. | Вызов блокируется до тех пор, пока не появится информация в канале связи и пока существует процесс, который может передать в него информацию. Если информация появилась, то процесс разблокируется, и поведение вызова определяется двумя предыдущими строками таблицы. Если в канал некому передать данные (нет ни одного процесса, у которого этот канал связи открыт для записи), то вызов возвращает значение 0. Если канал связи полностью закрывается для записи во время блокировки читающего процесса, то процесс разблокируется, и системный вызов возвращает значение 0. | |
Попытка читать из канала связи, в котором нет информации. Блокировка вызова не разрешена. | Если есть процессы, у которых канал связи открыт для записи, системный вызов возвращает значение -1 и устанавливает переменную errno в значение EAGAIN. Если таких процессов нет, системный вызов возвращает значение 0. | |
Системный вызов write | ||
Попытка записать в канал связи меньше байт, чем осталось до его заполнения. | Требуемое количество байт помещается в канал связи, возвращается записанное количество байт. | |
Попытка записать в канал связи больше байт, чем осталось до его заполнения. Блокировка вызова разрешена. | Вызов блокируется до тех пор, пока все данные не будут помещены в канал связи. Если размер буфера канала связи меньше, чем передаваемое количество информации, то вызов тем самым будет ждать, пока часть информации не будет считана из канала связи. Возвращается записанное количество байт. | |
Попытка записать в канал связи больше байт, чем осталось до его заполнения, но меньше, чем размер буфера канала связи. Блокировка вызова запрещена. | Системный вызов возвращает значение -1 и устанавливает переменную errno в значение EAGAIN. | |
В канале связи есть место. Попытка записать в канал связи больше байт, чем осталось до его заполнения, и больше, чем размер буфера канала связи. Блокировка вызова запрещена. | Записывается столько байт, сколько осталось до заполнения канала. Системный вызов возвращает количество записанных байт. | |
Попытка записи в канал связи, в котором нет места. Блокировка вызова не разрешена. | Системный вызов возвращает значение -1 и устанавливает переменную errno в значение EAGAIN. | |
Попытка записи в канал связи, из которого некому больше читать, или полное закрытие канала на чтение во время блокировки системного вызова. | Если вызов был заблокирован, то он разблокируется. Процесс получает сигнал SIGPIPE. Если этот сигнал обрабатывается пользователем, то системный вызов вернет значение -1 и установит переменную errno в значение EPIPE. | |
Необходимо отметить дополнительную особенность системного вызова write при работе с pip’ами и FIFO. Запись информации, размер которой не превышает размер буфера, должна осуществляться атомарно – одним подряд лежащим куском. Этим объясняется ряд блокировок и ошибок в предыдущем перечне. |
Задача повышенной сложности: определите размер pipe для вашей операционной системы.