C,让父母和子进程使用信号工作

我试图让我的子进程生成1到9之间的随机数,然后将其发送到我的父进程,每次按下control + Z时它都会显示它。 我也使用dup2()将printf和scanf中的流更改为每个进程的管道读写端。

代码如下所示,按控制C启动主程序。 现在,每当从子节点发送随机数并在父节点上显示时,它等待控制Z被按下。 我面临的问题是,似乎孩子只在再次按下控制Z时运行一次 ,它只运行父代码。 管道也只显示相同的数字,并且永远不会更改,因此不会更新。 我还注意到,如果我在dup函数printf(“a \ n”)之前删除了printf行,它会输出随机数….但是,运行一次的子进程的问题仍然存在。

如果有人可以帮助我,我将不胜感激。 干杯。

#include  #include  #include  #include  #include  #include  #include  int selection = 0; int secondSel = 0; int fd[2]; int pipe1; pid_t fork1; void handleSignal(int sig) { if (sig == SIGINT) { selection = 1; secondSel = 1; } if (sig == SIGTSTP) { selection = 1; } } int main() { int firstPipe[2]; int secondPipe[2]; //wait for control+C if (signal(SIGINT, handleSignal) == SIG_ERR) { printf("Error catching signal C \n"); exit(0); } while(1) { //wait till control c is pressed if (selection == 1) { signal(SIGINT, SIG_IGN); if (secondSel == 1) { pipe1 = pipe(fd); } if (pipe1 < 0) { printf("Error creating pipe 1 \n"); exit(1); } if (secondSel == 1) { fork1 = fork(); } if (fork1 < 0) { printf("Error with first fork. \n"); exit(1); } else if (fork1 == 0) //first child process { signal(SIGTSTP, handleSignal); pause(); printf("a \n"); int randNum1; close(fd[0]); dup2(fd[1], 1); randNum1 = rand() % 9 + 1; printf("%d ", randNum1); fflush(stdout); close(fd[1]); } else //parent { signal(SIGTSTP, handleSignal); pause(); printf("b \n"); int f; close(fd[1]); dup2(fd[0], 0); scanf("%d \n", &f); printf("%d \n", f); close(fd[0]); } secondSel = 0; } } } 

评论/代码:

 //wait for control+C if (signal(SIGINT, handleSignal) == SIG_ERR) 

是一个令人不安的开始。 signal()函数为SIGINT设置信号处理程序,但它无法等待信号到达。 最简单的解决方法是在该代码块之后添加对pause()的调用。

在无限循环内,代码:

  if (secondSel == 1) { pipe1 = pipe(fd); } if (pipe1 < 0) { printf("Error creating pipe 1 \n"); exit(1); } 

是次优的和/或混乱的。 由于pipe1仅在调用pipe()时设置,因此无需在每次迭代时对其进行测试。 应该在标准错误上报告错误消息,并且不应该有尾随空白。 代码应该是:

  if (secondSel == 1) { if (pipe(fd) < 0) { fprintf(stderr, "Error creating pipe 1\n"); exit(1); } } 

对保护fork()的同一变量进行了类似的测试。

您的代码在第一个循环后仔细关闭管道,但从不重新打开它。 这就是为什么第二次和后续迭代失败的原因。 如果您没有尝试在每个周期执行所有操作,那么您的代码会更好。 另外,使用标准输出来调试信息会遇到各种问题; 最好使用标准错误 - 特别是当标准输出不能正常工作时。

仪表代码

这是代码的检测版本。 err_syserr()函数几乎是通用的; 使用usleep()的条件是此代码所特有的,并确保终止的错误消息的输出通常是有序的。 我将测试放在将失败的函数调用上 - 第一个关闭在第二个循环中失败,因为管道描述符在第一个循环结束时全部关闭。 (请注意,在fork()之后重用pipe()没有帮助 - 父级和子级中的管道不会相互连接。)

 #include  #include  #include  #include  static void err_syserr(const char *fmt, ...); int selection = 0; int secondSel = 0; int fd[2]; int pipe1 = 0; pid_t fork1 = 0; static void handleSignal(int sig) { if (sig == SIGINT) { selection = 1; secondSel = 1; } if (sig == SIGTSTP) { selection = 1; } } int main(void) { // wait for control+C if (signal(SIGINT, handleSignal) == SIG_ERR) { printf("Error catching signal C\n"); exit(1); } //printf("Waiting for interrupt\n"); //pause(); while (1) { fprintf(stderr, "Looping: %d (%d)\n", (int)getpid(), selection); // wait till control c is pressed if (selection == 1) { signal(SIGINT, SIG_IGN); if (secondSel == 1) { pipe1 = pipe(fd); fprintf(stderr, "Created pipe: %d (%d, %d)\n", pipe1, fd[0], fd[1]); } if (pipe1 < 0) { printf("Error creating pipe 1\n"); exit(1); } if (secondSel == 1) { fork1 = fork(); fprintf(stderr, "Forked: %d (%d, %d)\n", fork1, (int)getpid(), (int)getppid()); } if (fork1 < 0) { printf("Error with first fork.\n"); exit(1); } else if (fork1 == 0) // first child process { signal(SIGTSTP, handleSignal); fprintf(stderr, "Pausing C: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused C: %d\n", (int)getpid()); printf("a\n"); if (close(fd[0]) != 0) err_syserr("close(fd[0]=%d) failed", fd[0]); if (dup2(fd[1], 1) < 0) err_syserr("dup2(fd[1]=%d, 1) failed", fd[1]); int randNum1 = rand() % 9 + 1; fprintf(stderr, "Print C: %d\n", randNum1); if (printf("%d\n", randNum1) < 2) { fprintf(stderr, "Print C: failed\n"); clearerr(stdout); } fflush(stdout); if (close(fd[1]) != 0) err_syserr("close(fd[1]=%d) failed", fd[1]); } else // parent { signal(SIGTSTP, handleSignal); fprintf(stderr, "Pausing P: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused P: %d\n", (int)getpid()); printf("b\n"); if (close(fd[1]) != 0) err_syserr("close(fd[1]=%d) failed", fd[1]); if (dup2(fd[0], 0) < 0) err_syserr("dup2(fd[0]=%d, 0) failed", fd[0]); int f = 99; if (scanf("%d", &f) != 1) { fprintf(stderr, "Scanf P: failed\n"); clearerr(stdin); } printf("Parent: %d\n", f); if (close(fd[0]) < 0) err_syserr("close(fd[0]=%d) failed", fd[0]); } secondSel = 0; } } return 0; } #include  #include  #include  static void err_syserr(const char *fmt, ...) { int errnum = errno; va_list args; if (fork1 != 0) /* Parent waits 1/4 second */ usleep(250000); fprintf(stderr, "%d: ", (int)getpid()); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); if (errnum != 0) fprintf(stderr, ": %d %s", errnum, strerror(errnum)); fputc('\n', stderr); exit(EXIT_FAILURE); } 

输出示例:

 $ ./sigbug Looping: 23528 (0) Looping: 23528 (0) … …huge numbers of 'looping' messages omitted… … Looping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) ^CLooping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) Created pipe: 0 (3, 4) Forked: 23529 (23528, 45428) Pausing P: 23528 Forked: 0 (23529, 23528) Pausing C: 23529 ^ZUnpaused C: 23529 Unpaused P: 23528 a b Print C: 5 Looping: 23529 (1) Pausing C: 23529 Parent: 5 Looping: 23528 (1) Pausing P: 23528 ^ZUnpaused P: 23528 b Unpaused C: 23529 23529: close(fd[0]=3) failed: 9 Bad file descriptor 23528: close(fd[1]=4) failed: 9 Bad file descriptor $ 

错误的固定代码

这是代码的修订版本,在我看来更合适的逻辑。 它不包括err_syserr()因为函数调用没有失败。 不需要selectionsecondSel变量; 信号处理程序成为一个只包含return;的存根return; (可以省略)。 它在开始时不会像疯了一样循环,因为我暂停了等待中断。

 #include  #include  #include  #include  static void handleSignal(int sig) { return; } int main(void) { int fd[2]; int pipe1 = 0; pid_t fork1 = 0; if (signal(SIGINT, handleSignal) == SIG_ERR) { printf("Error catching signal C\n"); exit(1); } printf("Waiting for interrupt\n"); pause(); signal(SIGINT, SIG_IGN); pipe1 = pipe(fd); if (pipe1 < 0) { fprintf(stderr, "Error creating pipe 1\n"); exit(1); } fprintf(stderr, "Created pipe: %d (%d, %d)\n", pipe1, fd[0], fd[1]); fork1 = fork(); if (fork1 < 0) { fprintf(stderr, "Error with fork.\n"); exit(1); } fprintf(stderr, "Forked: %d (%d, %d)\n", fork1, (int)getpid(), (int)getppid()); signal(SIGTSTP, handleSignal); if (fork1 == 0) { dup2(fd[1], 1); close(fd[0]); close(fd[1]); while (1) { fprintf(stderr, "Pausing C: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused C: %d\n", (int)getpid()); int randNum1 = rand() % 9 + 1; fprintf(stderr, "Print C: %d\n", randNum1); if (printf("%d\n", randNum1) < 2) { fprintf(stderr, "Print C: failed\n"); clearerr(stdout); } fflush(stdout); } } else { dup2(fd[0], 0); close(fd[0]); close(fd[1]); while (1) { fprintf(stderr, "Pausing P: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused P: %d\n", (int)getpid()); int f = 99; if (scanf("%d", &f) != 1) { fprintf(stderr, "Scanf P: failed\n"); clearerr(stdin); } else printf("Parent: %d\n", f); } } return 0; } 

输出示例:

 $ ./sigbug-jl1 Waiting for interrupt ^CCreated pipe: 0 (3, 4) Forked: 23554 (23553, 45428) Pausing P: 23553 Forked: 0 (23554, 23553) Pausing C: 23554 ^ZUnpaused C: 23554 Print C: 5 Unpaused P: 23553 Pausing C: 23554 Parent: 5 Pausing P: 23553 ^ZUnpaused C: 23554 Print C: 8 Unpaused P: 23553 Pausing C: 23554 Parent: 8 Pausing P: 23553 ^ZUnpaused P: 23553 Unpaused C: 23554 Print C: 6 Pausing C: 23554 Parent: 6 Pausing P: 23553 ^\Quit: 3 $ 

我使用SIGQUIT来终止程序,因为中断被禁用。

您遇到的问题是在第一个号码从子节点发送到父节点后关闭管道。

但是,主要问题是您的代码混乱,这使得更难以弄清楚发生了什么并增加了出错的可能性。 我建议重构您的代码,而不是将所有内容放在一个大循环中,而不是:

 wait for SIGINT create pipe fork if child: set up stdout using dup2() while true: wait for SIGTSTP write to pipe else: set up stdin using dup2() while true: wait for SIGTSTP read from pipe etc.