在我的代码中,我应该“等到孩子们完成”?我有一个类似于自定义shell的C程序。现在我有一个内置函数checkEnv
,可以打印已排序的环境变量。所以我可以启动我的shell并列出我的环境变量:
$ ./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);
}
现在我想在使用寻呼机列出环境变量时捕获输出终止信号,以便程序返回到自定义shell而不是终止整个程序。所以我想我应该使用一些信号处理但是如何和哪个信号?
实际的fork来自此函数。
/* 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
中运行我的shell,我会正常退出。
(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)
答案 0 :(得分:1)
您希望抓住SIGCHLD
,并使用其中一个wait()
函数获取子状态。
以下信息来自online tutorial。
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); }
答案 1 :(得分:1)
在父母中,您不需要接收任何信号,只需调用wait()直到它返回您的孩子已经死亡并且现在被收获。您的shell现在已准备好执行新任务。
原始请求中的代码如果非常不完整,那么这里有一个例子:
#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;
}