Интересно. Даже если это помечено Linux, я выйду на конечность и скажу, что вы используете это на OS X.
При компиляции в Linux проблема не существует, но на Mac это происходит точно так, как вы описали , Это похоже на ошибку в OS X: поскольку и процесс оболочки, и cat(1)
находятся в одной группе процессов (так как вы явно не меняете членство в группе), похоже, что OS X делает ошибку в подаче следующей строки ввода fgets(3)
, который спит в процессе cat(1)
, поэтому вы теряете эту линию ввода из процесса оболочки (потому что она потребляется спящим cat(1)
).
Причина, по которой это не происходит с bash, заключается в том, что bash поддерживает управление заданиями, и поскольку такие процессы помещаются в отдельные группы процессов (в частности, bash выбирает первый процесс конвейера процесса как лидера группы процессов). Поэтому, когда вы делаете то же самое в bash, каждый вызов cat(1)
заканчивается тем, что помещает его в отдельную группу процессов (а затем оболочка контролирует, какая группа процессов находится на переднем плане с tcsetpgrp(3)
). Таким образом, в любое время ясно, какая группа процессов имеет контроль над терминальным входом; когда вы приостановите cat(1)
в bash, группа процессов переднего плана снова изменится на bash, и ввод будет считан успешно.
Если вы делаете то же самое, что и bash в своей оболочке, он будет работать в Linux, OS/X и, в основном, в любом другом варианте UNIX (и это делают другие оболочки).
На самом деле, если вы хотите, чтобы у вашей оболочки была поддержка работы, вам нужно будет это сделать рано или поздно (узнайте о группах процессов, сеансах, tcsetpgrp(3)
, setpgid(2)
и т. Д.).
Так, в общем, делать правильные вещи, если вы хотите поддержку работы и обернуть раздвоенный процесс в новой группе процессов:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
void interpreter() {
char input[256];
char dir[PATH_MAX+1];
char *argv[256];
int argc = 0;
char *token;
if (getcwd(dir, PATH_MAX+1) == NULL) {
//error occured
exit(0);
}
printf("[shell:%s]$ ", dir);
fgets(input,256,stdin);
if (strlen(input) == 0) {
exit(0);
}
input[strlen(input)-1] = 0;
if (strcmp(input,"") == 0) {
return;
}
token = strtok(input, " ");
while(token && argc < 255) {
argv[argc++] = token;
token = strtok(NULL, " ");
}
argv[argc] = 0;
pid_t forknum = fork();
if (forknum != 0) {
setpgid(forknum, forknum);
signal(SIGTTOU, SIG_IGN);
tcsetpgrp(STDIN_FILENO, forknum);
tcsetpgrp(STDOUT_FILENO, forknum);
int status;
waitpid(forknum, &status, WUNTRACED);
tcsetpgrp(STDOUT_FILENO, getpid());
tcsetpgrp(STDIN_FILENO, getpid());
} else {
setpgid(0, getpid());
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
setenv("PATH","/bin:/usr/bin:.",1);
execvp(argv[0], argv);
if (errno == ENOENT) {
printf("%s: command not found\n", argv[0]);
} else {
printf("%s: unknown error\n", argv[0]);
}
exit(0);
}
}
int main() {
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
while(1) {
interpreter();
}
}
(Хотя, по общему признанию, это прискорбно, что OS X делает такое бедная работа в этой ситуации - вы действительно не должны иметь, чтобы сделать это).
Изменения происходят только внутри кода, специфичного для процесса: как дочерний, так и родительский вызов setpgid(2)
, чтобы убедиться, что процесс новорожденного действительно находится в одной группе процессов, прежде чем либо родительский процесс сам предполагает, что это уже true (этот шаблон рекомендуется в Расширенное программирование в среде UNIX); вызов tcsetpgrp(3)
должен вызываться родителем.
Конечно, это далеко не полный, вам необходимо запрограммировать необходимые функции, чтобы вернуть работу на передний план, задание списка и т. Д. Но, тем не менее, код выше работает с вашим тестовым сценарием.
Nitpick: вы должны использовать sigaction(2)
вместо устаревшего, ненадежного и зависящего от платформы signal(3)
, но это незначительная проблема.
Это очень неясно: * следующая строка ввода будет по-прежнему идти к кошке, а не к моей основной * ... –
Позвольте мне привести пример. После того, как программа cat приостановлена, я набираю «cat», другой кот должен быть запущен, но ничего не происходит. Если я просыпаюсь с предыдущей программой кошки, она сразу же выводит «cat». – vincentzhou
Можете ли вы опубликовать полный код, ожидаемое поведение и поведение, которое вы получаете? –