Intereting Posts
sizeof union больше, чем ожидалось. как здесь происходит выравнивание по типу? Смущение над вызовом функции в синтаксисе pre-ANSI C Переменные начальные нули в C99 printf Сегментация Неисправность при попытке напечатать значение в C Цвета отключены в программе SDL Как найти индекс символа внутри строки в C? Определите, является ли функция безопасной с использованием асинхронного сигнала (может быть вызвана внутри обработчика сигнала) Почему и когда использовать статические структуры в программировании на С? начальный адрес массива a и & Как использовать `offsetof` для доступа к полю стандартным образом? Библиотеки C для операций с математической матрицей Оператор switch с использованием строки в массиве Локальные и статические переменные в C C: Как я могу использовать единый массив указателей функций для функций с переменными параметрами? Пример исходного кода из «Linux kernel programming»

fgets () вызов с redirectм получает ненормальный stream данных

Я собирался написать оболочку с языком C. Ниже приведен исходный код:

#include  #include  #include  #include  #include  int getcmd(char *buf, int nbuf) { memset(buf, 0, nbuf); fgets(buf, nbuf, stdin); printf("pid: %d, ppid: %d\n", getpid(), getppid()); printf("buf: %s", buf); if(buf[0] == 0) {// EOF printf("end of getcmd\n"); return -1; } return 0; } int main(void) { static char buf[100]; int fd, r, ret; // Read and run input commands. while((ret = getcmd(buf, sizeof(buf))) >= 0){ if(fork() == 0) exit(0); wait(&r); } exit(0); } 

Когда я исполняю скомпилированный исполняемый файл с redirectм stdin в файл с именем t.sh, который имеет значение «1111 \ n2222 \ n», например ./myshell <t.sh, вывод:

 pid: 2952, ppid: 2374 buf: 1111 pid: 2952, ppid: 2374 buf: 2222 pid: 2952, ppid: 2374 buf: 2222 pid: 2952, ppid: 2374 buf: end of getcmd 

Очевидно, функция getcmd () получает 3 строки (1111, 2222, 2222), тогда как в t.sh. И эта ситуация становится еще хуже при размещении большего количества строк в t.sh.

И основной процесс – это единственный процесс, выполняемый getcmd, который мы можем сказать по выводу pid.

Кстати, я считаю, что если строка кода wait (& r) удалена, выход может стать нормальным.

wait гарантирует, что дочерний процесс получит время для запуска до того, как родитель закончит файл. Если я strace файл под Linux, я получаю

 % strace -f ./a.out [lots of stuff] wait4(-1, strace: Process 29317 attached  [pid 29317] lseek(0, -2, SEEK_CUR) = 0 [pid 29317] exit_group(0) = ? [pid 29317] +++ exited with 0 +++ <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29317 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=29317, si_uid=1000, si_status=0 _utime=0, si_stime=0} --- [lots of stuff] 

Детский процесс перематывает стандартный ввод как одну из первых операций после fork , после чего он будет быстро выйти. В частности, он перематывает назад столько же байтов из streamа, сколько было прочитано в fgets в буфер, но все еще не используется . libc делает это автоматически после fork. Я также видел, как дочерний процесс смывал stdout .

Я не уверен, что об этом думать … но ясно, что если вы хотите написать оболочку, вы вообще не должны взаимодействовать со стандартными streamами с помощью . Если lseek не произошел, тогда дочерний процесс будет пропущен до 4095 байтов stdin ! Вы всегда должны использовать только read и write из . Кроме того, вам может быть повезло с добавлением следующего вызова в начало main прежде чем что-либо будет прочитано из stdin :

 if (setvbuf(stdin, NULL, _IONBF, 0) != 0) { perror("setvbuf:"); exit(1); } 

Это установит stream stdin в небуферизованный режим , чтобы он не читал слишком много. Тем не менее, на странице руководства Linux для fgets говорится:

Не рекомендуется смешивать вызовы с функциями ввода из библиотеки stdio с низкоуровневыми вызовами для чтения (2) для файлового дескриптора, связанного с входным streamом; результаты будут неопределенными и, скорее всего, не то, что вы хотите.

BTW, это не может быть воспроизведено, если stdin происходит из трубы:

 % echo -e '1\n2' | ./a.out pid: 498, ppid: 21285 buf: 1 pid: 498, ppid: 21285 buf: 2 pid: 498, ppid: 21285 buf: end of getcmd 

Но, естественно, это делает другую проблему видимой – что ребенок видит, что вход пропускается.


PS

Вы никогда не проверяете возвращаемое значение fgets чтобы не знать, когда возникает ошибка чтения.

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