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

Я пытаюсь создать простую программу оболочки в 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 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 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 Я буду реализовывать его так, чтобы каждая последующая команда определяла логическое значение "фона", извините за путаницу

Ответы

Ответ 1

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

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

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

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

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