Магия потоков в Linux. Когда закончить? - PullRequest
0 голосов
/ 01 июня 2019

Сегодня в 5 утра я прочитал статью о read системном вызове.И все становится значительно яснее для меня.

ssize_t read (int fd, void * buf, size_t count);

Конструкция *nix подобной операционной системы удивительна своей простотой.Файловый интерфейс для любого объекта, просто попросите записать некоторую дату из этого fd интерфейса в некоторую память по указателю *buf.Все равно для сети, файлов, потоков.

Но возникает какой-то вопрос. Как различить два случая ?: 1) Поток пуст, нужно дождаться новых данных.2) Поток закрыт, необходимо закрыть программу.

Вот сценарий:

  • Чтение данных из STDIN в цикле, это STDIN перенаправлено на pipe.
  • некоторые text_data появляются
  • просто читайте кусочек за кусочком до того, что EOF в памяти, или 0 в результате вызова read?
  • Как программа поймет: ждать нового ввода или выхода?

Это неясно.В случае endless или continuous потоков.

UPD После разговора с @Bailey Kocin и прочтения некоторых документов я понимаю это.Исправьте меня, если я ошибаюсь.

  • read задерживает выполнение программы и ожидает count количество укусов.
  • Когда появляется count количество укусов read записывает в buf и выполнение продолжается.
  • Когда stream закрывается read возвращает 0, и это сигнал о том, что программа может быть завершена.

Вопрос Есть ли EOF в buf?

UPD2 EOF - это константа, которая может быть на выходе функции getc

 while (ch != EOF)     { 
    /* display contents of file on screen */ 
    putchar(ch);  

    ch = getc(fp);   
 }

Но в случае read значение дозы EOF не появляется в buf.read системный вызов сигнализирует об окончании файла, возвращая 0.Вместо записи EOF константа в data-area, как и в случае getc.

EOF - это константа, которая варьируется в разных системах.И это используется для getc.

1 Ответ

2 голосов
/ 01 июня 2019

Давайте сначала разберемся с вашим первоначальным вопросом. Обратите внимание, что man 7 pipe должен дать некоторую полезную информацию об этом.

Скажем, у нас стандартный ввод перенаправлен на сторону ввода дескриптора, созданного с помощью вызова pipe, например:

pipe(p);
// ... fork a child to write to the output side of the pipe ...
dup2(p[0], 0);  // redirect standard input to input side

и мы звоним:

bytes = read(0, buf, 100);

Во-первых, обратите внимание, что это ведет себя не иначе, чем простое чтение непосредственно из p[0], поэтому мы могли бы просто сделать:

pipe(p);
// fork child
bytes = read(p[0], buf, 100);

Тогда, по сути, есть три случая:

  1. Если в канале есть байты (т. Е. Хотя бы один байт был записан, но еще не прочитан), то вызов чтения немедленно вернется и вернет все доступные байты, максимум до 100 байт. Возвращаемым значением будет количество прочитанных байтов, и оно всегда будет положительным числом от 1 до 100.

  2. Если канал пуст (без байтов) и выходная сторона закрыта, буфер не будет затронут, и вызов немедленно вернется с возвращаемым значением 0.

  3. В противном случае вызов чтения будет блокироваться до тех пор, пока что-то не будет записано в канал, или если выходная сторона не будет закрыта, а затем вызов чтения немедленно вернется с использованием правил в случаях 1 и 2.

Итак, если вызов read() возвращает 0, это означает, что достигнут конец файла, и больше не ожидается никаких байтов. Ожидание дополнительных данных происходит автоматически, и после ожидания вы получите либо данные (положительное возвращаемое значение), либо сигнал конца файла (нулевое возвращаемое значение). В особом случае, когда другой процесс записывает несколько байтов, а затем немедленно закрывает (выходную сторону) канал, следующий вызов read() вернет положительное значение до указанного count. Последующие read() вызовы продолжат возвращать положительные значения, пока есть больше данных для чтения. Когда данные исчерпаны, вызов read() вернет 0 (поскольку канал закрыт).

В Linux вышеприведенное всегда верно для каналов и любых положительных значений count. Там могут быть различия для вещей других , чем трубы. Кроме того, если count равно 0, вызов read() всегда будет возвращаться немедленно с возвращаемым значением 0. Обратите внимание, что если вы пытаетесь написать код, работающий на платформах, отличных от Linux, вам, возможно, следует быть более осторожным. Реализация может возвращать ненулевое число байтов меньше, чем запрошенное число, даже если в конвейере доступно больше байтов - это может означать, что существует предел, определенный реализацией (поэтому вы никогда не получите больше 4096 байтов, независимо от того, сколько вы запрашиваете, например) или что это ограничение, определяемое реализацией, изменяется от вызова к вызову (поэтому, если вы запрашиваете байты за границей страницы в буфере ядра, вы получаете только конец страницы или что-то в этом роде). В Linux ограничений нет - вызов read всегда вернет все доступное значение до count, независимо от того, насколько велик count.

В любом случае, идея заключается в том, что что-то вроде следующего кода должно надежно считывать все байты из канала до тех пор, пока выходная сторона не будет закрыта, даже на платформах, отличных от Linux:

#define _GNU_SOURCE 1
#include <errno.h>
#include <unistd.h>

/* ... */

    while ((count = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)))) > 0) {
        // process "count" bytes in "buffer"
    }
    if (count == -1) {
        // handle error
    }
    // otherwise, end of data reached

Если канал никогда не закрывается («бесконечный» или «непрерывный» поток), цикл while будет работать вечно, потому что read будет блокироваться, пока не вернет ненулевой счетчик байтов.

Обратите внимание, что труба также может быть переведена в неблокирующий режим, что существенно меняет поведение, но приведенное выше является режимом блокировки по умолчанию.

Относительно ваших UPD вопросов:

Да, read задерживает выполнение программы до тех пор, пока данные не станут доступны, но NO , он не обязательно ожидает count байтов. Он будет ожидать хотя бы одного непустого write в трубу, и это разбудит процесс; когда процесс получит возможность запуска, он вернет все доступные до , но не обязательно равные count байтов. Обычно это означает, что если другой процесс записывает 5 байтов, заблокированный вызов read(fd, buffer, 100) вернет 5 и выполнение продолжится. Да, если read возвращает 0, это сигнал о том, что больше нет данных для чтения, и сторона записи канала была закрыта (поэтому больше никаких данных никогда не будет доступно). Нет , значение EOF не отображается в буфере. Там будут отображаться только считанные байты, и буфер не будет затронут, когда read() вернет 0, поэтому он будет содержать все, что было до вызова read().

Относительно вашего UPD2 комментария:

Да, в Linux EOF - это константа, равная целому числу -1. (Технически, в соответствии со стандартом C99, это целочисленная константа, равная отрицательному значению; возможно, кто-то знает платформу, где она отличается от -1.) Эта константа не используется интерфейсом read(), и она конечно не записывается в буфер. Хотя read() возвращает -1 в случае ошибки, было бы плохой практикой сравнивать возвращаемое значение из read() с EOF вместо -1. Как вы заметили, значение EOF действительно используется только для функций библиотеки C, таких как getc() и getchar(), чтобы отличить конец файла от успешно прочитанного символа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...