Системный вызов read() позволяет считывать данные из открытого файла, на который ссылается дескриптор fd.
#include <unistd.h>
ssize_t read(int fd, void *buffer, size_t count);
Возвращает количество считанных байтов, 0 при EOF или –1 при ошибке
Аргумент count определяет максимальное количество считываемых байтов (тип данных size_t — беззнаковый целочисленный). Аргумент buffer предоставляет адрес буфера памяти, в который должны быть помещены входные данные. Этот буфер должен иметь длину в байтах не менее той, что задана в аргументе count.
Системные вызовы не выделяют память под буферы, которые используются для возвращения информации вызывающему процессу. Вместо этого следует передать указатель на ранее выделенный буфер памяти подходящего размера. Этим вызовы отличаются от ряда библиотечных функций, которые выделяют буферы в памяти с целью возвращения информации вызывающему процессу.
При успешном вызове read() возвращается количество фактически считанных байтов или 0, если встретился символ конца файла. При ошибке обычно возвращается –1. Тип данных ssize_t относится к целочисленному типу со знаком. Этот тип используется для хранения количества байтов или значения –1, которое служит признаком ошибки.
При вызове read() количество считанных байтов может быть меньше запрашиваемого. Возможная причина для обычных файлов — близость считываемой области к концу файла.
При использовании вызова read() в отношении других типов файлов, например конвейеров, FIFO-устройств, сокетов или терминалов, также могут складываться различные обстоятельства, при которых количество считанных байтов оказывается меньше запрашиваемого. Например, изначально применение read() в отношении терминала приводит к считыванию символов только до следующего встреченного символа новой строки (\n). Эти случаи будут рассматриваться при изучении других типов файлов далее в книге.
При использовании read() для ввода последовательности символов из, скажем, терминала, можно предполагать, что сработает следующий код:
#define MAX_READ 20
char buffer[MAX_READ];
if (read(STDIN_FILENO, buffer, MAX_READ) == -1)
errExit("read");
printf("The input data was: %s\n", buffer);
Этот фрагмент кода выведет весьма странные данные, поскольку в них, скорее всего, будут включены символы, дополняющие фактически введенную строку. Дело в том, что вызов read() не добавляет завершающий нулевой байт в конце строки, которая задается для вывода функции printf(). Нетрудно догадаться, что именно так и должно быть, поскольку read() может использоваться для чтения любой последовательности байтов из файла. В некоторых случаях входные данные могут быть текстом, но бывает, что это двоичные целые числа или структуры языка C в двоичном виде. Невозможно «объяснить» вызову read() разницу между ними, поэтому он не в состоянии выполнять соглашение языка C о завершении строки символов нулевым байтом. Если в конце буфера входных данных требуется наличие завершающего нулевого байта, его нужно вставлять явным образом:
char buffer[MAX_READ + 1];
ssize_t numRead;
numRead = read(STDIN_FILENO, buffer, MAX_READ);
if (numRead == -1)
errExit("read");
buffer[numRead] = '\0';
printf("The input data was: %s\n", buffer);
Поскольку для завершающего нулевого байта требуется байт памяти, размер буфера должен быть как минимум на один байт больше максимальной предполагаемой считываемой строки.
Системный вызов write() записывает данные в открытый файл.
#include <unistd.h>
ssize_t write(int fd, const void *buffer, size_t count);
Возвращает количество записанных байтов или –1 при ошибке
Аргументы для write() аналогичны тем, что использовались для read(): buffer представляет собой адрес записываемых данных, count является количеством записываемых из буфера данных, а fd содержит дескриптор файла, который ссылается на тот файл, куда будут записываться данные.
В случае успеха вызов write() возвращает количество фактически записанных данных, которое может быть меньше значения аргумента count. Для дискового файла возможными причинами такой частичной записи может оказаться переполнение диска или достижение ограничения ресурса процесса на размеры файла. (Речь идет об ограничении RLIMIT_FSIZE, которое рассматривается в разделе 36.3.)
При выполнении ввода-вывода в отношении дискового файла успешный выход из write() не гарантирует перемещения данных на диск, поскольку ядро занимается буферизацией дискового ввода-вывода, чтобы сократить объем работы с диском и ускорить выполнение системного вызова write(). Более подробно этот вопрос рассматривается в главе 13.