С каким сигналом мне следует обращаться и как?
Где в моем коде я должен "ждать, пока дети не закончат"? У меня есть программа на C, похожая на пользовательскую оболочку. Теперь у меня есть встроенная функция checkEnv
который может печатать отсортированные переменные окружения. Так что я могу запустить свою оболочку и перечислить переменные окружения:
$ ./a.out
miniShell>> checkEnv
"'><;|&(:
_=./a.out
CLUTTER_IM_MODULE=xim
COMPIZ_CONFIG_PROFILE=ubuntu
COMP_WORDBREAKS=
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-mh5oMhyCI6
DEFAULTS_PATH=/usr/share/gconf/ubuntu.default.path
DESKTOP_SESSION=ubuntu
Код, который делает это следующим образом:
if(StartsWith(line, "checkEnv")) {
built_in_command=1;
pagerValue = getenv ("PAGER");
if (! pagerValue) {
if (ret == 0) {
pager_cmd[0]="less";
} else {
pager_cmd[0]="more";
}
}
else {
pager_cmd[0]=pagerValue;
}
if(i==1) {
cmd[0].argv= printenv;
cmd[1].argv= sort;
cmd[2].argv= pager_cmd;
fork_pipes(3, cmd);
}
else {
for (k = 1; k < i; k++)
{
len += strlen(argv2[k]) + 2;
}
tmp = (char *) malloc(len);
tmp[0] = '\0';
for (k = 1; k < i; k++)
{
pos += sprintf(tmp + pos, "%s%s", (k == 1 ? "" : "|"), argv2[k]);
}
grep[0]="grep";
grep[1]="-E";
grep[2]= tmp;
grep[3]= NULL;
cmd2[0].argv= printenv;
cmd2[1].argv= grep;
cmd2[2].argv= sort;
cmd2[3].argv= pager_cmd;
fork_pipes(4, cmd2);
free(tmp);
}
Теперь я хочу перехватить выходной сигнал завершения при выводе списка переменных среды с помощью пейджера, чтобы программа возвращалась в пользовательскую оболочку вместо завершения всей программы. Итак, я полагаю, что я должен использовать некоторую обработку сигналов, но как и какой сигнал?
Фактическая вилка из этой функции.
/* Helper function that forks pipes */
void fork_pipes(int n, struct command *cmd) {
int i;
int in = 0;
int fd[2];
/** loop and fork() */
for (i = 0; i < n - 1; ++i) {
if (pipe(fd) == -1) {
err_syserr("Failed creating pipe");
}
spawn_proc(in, fd[1], cmd + i);
close(fd[1]);
in = fd[0];
}
if (dup2(in, 0) < 0) {
err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
}
fprintf(stderr, "%d: executing %s\n", (int) getpid(), cmd[i].argv[0]);
execvp(cmd[i].argv[0], cmd[i].argv);
err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}
Куда должна идти обработка сигналов? Где минимальный рабочий пример того, что я пытаюсь сделать? Я не вижу ни одного примера, который делает это, и я думаю, что документация ужасна, только фрагменты и не полный пример.
Моя вспомогательная функция
/* Helper function that spawns processes */
int spawn_proc(int in, int out, struct command *cmd) {
pid_t pid;
pid = fork();
if (pid == 0) {
if (in != 0) {
if (dup2(in, 0) < 0)
err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
close(in);
}
if (out != 1) {
if (dup2(out, 1) < 0)
err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
close(out);
}
printf("** we are executing parent ***");
fprintf(stderr, "%d: executing %s\n", (int) getpid(), cmd->argv[0]);
execvp(cmd->argv[0], cmd->argv);
err_syserr("failed to execute %s: ", cmd->argv[0]);
}
else if (pid < 0) {
err_syserr("fork failed: ");
} else {
/* */
printf("** we are the parent ***");
}
return pid;
}
мой main()
теперь выглядит так:
int main(int argc, char *argv[]) {
sourceCount = 0;
const char *commandFile;
commandFile = NULL;
char *pathValue;
/* struct sigaction sa, osa;
struct sigaction sa2;*/
int errflag;
int cOption;
struct sigaction action;
/* use getopt_long() */
char *argv1[] = {"version", "par2", 0};
/* char *argv2[] = {"help", "-m", "arg1", 0};*/
/* use sigaction */
sigemptyset(&action.sa_mask);
action.sa_handler = handle_sigchld;
action.sa_flags = 0;
sigaction(SIGPIPE, &action, NULL); //Not work with kill -13 process_id
//works well
sigaction(SIGINT, &action, NULL); //work with kill -2 process_id
errflag = 0;
/* use getopt_long() */
while ((cOption = getopt(2, argv1, "m:t:n:fs?")) != -1) {
switch (cOption) {
case 'a':
printf("apples\n");
break;
case 'b':
printf("bananas\n");
break;
case 't':
printf("tree = %s\n", optarg);
break;
case '?':
++errflag;
break;
}
}
/*
while (( cOption = getopt (3, argv2, "m:t:n:fs?")) != -1) {
switch (cOption) {
case 'm':
printf("\n Help msg : %s \n", optarg);
exit(0);
case '?':
printf("\n -? Arg : %s \n", optarg);
break;
case 'n':
printf("\n -n Arg : %s \n", optarg);
break;
}
}
*/
/* get the PATH environment to find if less is installed */
pathValue = getenv("PATH");
if (!pathValue || getenv("PATH") == NULL) {
printf("'%s' is not set.\n", "PATH");
/* Default our path if it is not set. */
putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
}
else {
printf("'%s' is set to %s.\n", "PATH", pathValue);
}
exec_program(commandFile);
return (0);
}
Если я запускаю свою оболочку в gdb
Я получаю нормальный выход.
(gdb) run
Starting program: /home/dac/ClionProjects/shell2/openshell/shell
'PATH' is set to /home/dac/proj/google-cloud-sdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin.
dac:/home/dac/ClionProjects/shell2/openshell $ checkenv
7429: executing printenv
7430: executing grep
7417: executing less
7431: executing sort
process 7417 is executing new program: /bin/less
[Inferior 1 (process 7417) exited normally]
(gdb)
2 ответа
Родителю не нужно получать никаких сигналов, просто вызывайте wait(), пока он не вернет, что ваш ребенок умер и теперь пожинает. Ваша оболочка готова для выполнения новой задачи.
Ваш код в исходном запросе, если он очень неполный, вот один из примеров:
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
void foobar()
{
int pipe_fd[2];
pid_t pid1, pid2;
pipe(pipe_fd);
pid1=fork();
if (pid1 == 0)
{
/* child1 - let us pretend that we wanted to replace stdin and this child */
close (0);
dup(pipe_fd[0]);
close(pipe_fd[0]);
close(pipe_fd[1]);
execlp("wc", "wc", NULL);
perror ("execlp(wc)");
_exit(0);
}
pid2=fork();
if (pid2 == 0)
{
/* child - let us pretent that we wanted to replace stdout */
close (1);
dup(pipe_fd[1]);
close(pipe_fd[0]);
close(pipe_fd[1]);
execlp("ls", "ls", "-l", NULL);
perror ("execlp(ls)");
_exit(0);
}
close(pipe_fd[0]);
close(pipe_fd[1]);
/* wait until children are finished */
while ((pid1 >= 0) || (pid2 >= 0))
{
pid_t pid;
int status;
pid = wait(&status);
if (pid < 0)
{
continue;
}
if (pid == pid1)
{
pid1 = -1;
}
if (pid == pid2)
{
pid2 = -1;
}
}
}
int main(int argc, char *argv[])
{
foobar();
return 0;
}
Вы хотите поймать SIGCHLD
и пожинать статус ребенка с одним из wait()
функции.
Следующая информация взята из онлайн-урока.
void handle_sigchld(int sig) { int saved_errno = errno; while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {} errno = saved_errno; } struct sigaction sa; sa.sa_handler = &handle_sigchld; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, 0) == -1) { perror(0); exit(1); }
Кроме того, вы, вероятно, захотите игнорировать SIGPIPE
,
struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGPIPE, &sa, 0) == -1) { perror(0); exit(1); }