Что касается фоновых процессов с использованием fork () и дочерних процессов в моей фиктивной оболочке - PullRequest
18 голосов
/ 30 ноября 2011

Я пытаюсь создать простую программу оболочки на C. Мне нужно, чтобы она выдавала пользователю подсказку, в которой он может запускать другие локальные программы. Я могу хорошо выполнить эту часть, используя fork (), в котором родительский процесс ждет () дочернего, а дочерний execvp () - программа.

Однако, если символ '&' добавлен в конец команды пользователя, мне нужно, чтобы его программа работала в фоновом режиме, то есть мне нужно, чтобы родитель НЕ ожидал дочернего процесса, а вместо этого немедленно возвратил приглашение пользователь, позволяя продолжить фоновый процесс, но не позволяя ему отображать что-либо на экране. Я просто хочу иметь возможность проверить, что он все еще существует с помощью команды PS.

Я попытался понять идею, лежащую в основе использования fork () для создания дочернего элемента, затем снова вызвать дочерний fork () для создания своего рода внука, а затем сразу же завершить () - дочерний процесс. то есть сирота внука. Предположительно, это позволяет родителю все еще ждать ребенка, но поскольку ребенок фактически заканчивается почти сразу, это как будто он вообще не ждет? Что-то про безумие зомби? Я не знаю. Некоторые сайты, с которыми я сталкивался, рекомендуют это как способ запуска процесса в фоновом режиме.

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

Это моя реализация кода, которая, я уверен, совершенно неверна. Мне просто интересно, является ли эта целая штука внуков даже тем путем, по которому мне нужно идти, и если да, что не так с моим кодом?

36 int main(int argc, char *argv[])
37 {
38     char buffer[512];
39     char *args[16];
40     int background;
41     int *status;
42     size_t arg_ct;
43     pid_t pid;
44 
45     while(1)
46     {
47         printf("> ");
48         fgets(buffer, 512, stdin);
49         parse_args(buffer, args, 16, &arg_ct);
50 
51         if (arg_ct == 0) continue;
52 
53         if (!strcmp(args[0], "exit"))
54         {
55             exit(0);
56         }
57 
58         pid = fork();  //here I fork and create a child process
61 
62         if (pid && !background)  //i.e. if it's the parent process and don't need to run in the background (this is the part that seems to work)
63         {
64             printf("Waiting on child (%d).\n", pid);
65             pid = wait(status);
66             printf("Child (%d) finished.\n", pid);
67         }
68         else
69         {
70             if (background && pid == 0) { //if it's a child process and i need to run the command in background
71                
72                 pid = fork(); //fork child and create a grandchild
73                 if (pid) exit(0); //exit child and orphan grandchild
74                 
75                 execvp(args[0], args); //orphan execs the command then exits
76                 exit(1);
77 
78             } else exit(0);
79         }
80     }
81     return 0;
82 }

P.S. Чтобы было ясно, мне нужен процесс, который я запускаю в фоновом режиме, чтобы никогда больше не издавать звук, даже если он имеет бесконечный цикл операторов печати или что-то в этом роде. Я просто хочу убедиться, что он все еще работает в фоновом режиме через ps -a или что-то еще.

Извините за смутное объяснение. Я просто не знаю, как объяснить это лучше.

Заранее спасибо

P.P.S Я буду реализовывать это так, чтобы каждая последующая команда определяла логическое значение 'background', извините за путаницу

1 Ответ

22 голосов
/ 30 ноября 2011

Вы не хотите использовать метод double- fork() в оболочке, то есть для написания демонов, которые специально хотят избежать контроля со стороны оболочки, которая их запускала.

Вместо этого способ дублировать поведение & в существующих оболочках состоит в том, чтобы вызвать fork(), а затем в дочернем вызове setpgid(0, 0);, чтобы поместить дочернего элемента в новую группу процессов . Родитель просто продолжает (возможно, после печати PID дочернего элемента) - он не вызывает wait().

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

Если группа фоновых процессов пытается прочитать с терминала, ей будет отправлен сигнал, чтобы остановить его. Вывод на терминал все еще разрешен - это нормально (попробуйте ls & в вашей обычной оболочке). Для реализации команды fg в вашей оболочке вам понадобится функция tcsetpgrp() для изменения группы процессов переднего плана.

Вы также захотите регулярно вызывать waitpid() с опцией WNOHANG - скажем, непосредственно перед отображением приглашения оболочки. Это позволит вам определить, когда фоновый процесс завершился или остановился (или, альтернативно, вы можете обработать сигнал SIGCHLD).

...