我有一个家庭作业,我应该写一个C程序,生成一个孩子。父级打开一个文件,通过读取每一行,打印行号和行内容,然后倒带文件,从而永远循环。
子进程以随机间隔向父进程发送SIGUSR1信号。为了创建这些间隔,我选择让孩子睡觉。
当接收到第一个信号时,父进程跳过print语句。当接收到另一个SIGUSR1信号时,它再次开始打印。因此,它会一直持续下去,直到发送了随机数量的信号。然后它忽略五个信号。在忽略五个信号之后,父进程打印接收到的信号的数量,并在接收到另一个信号后退出。
我的问题是,当子进程发出信号时,程序会崩溃。父进程有信号处理程序,但它们从未使用过。我犯了什么错误?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void checkArguments(int nbrOfArg);
static void sig_exit(int signo);
static void sig_handler(int signo);
int sigcount, n;
int main(int argc, char *argv[]){
int interval, i, linecount = 1;
char buffer[1024];
FILE *f;
sigcount = 0;
signal(SIGUSR1, sig_handler);
srand(time(NULL)); // generate random numbers
n = rand() % 20 + 10;
if(fork() != 0){ //parent process
printf("%d\n", getpid()); //debugging
checkArguments(argc);
f = fopen(argv[1], "r"); //open file
if(f == NULL){
printf("Error opening file\n");
exit(EXIT_FAILURE);
}
if(sigcount < n){ //n is random number of signals sent that starts/stops printing
signal(SIGUSR1, sig_handler); //set signal handler
do {
while(!feof(f)){// infinite printing loop
fgets(buffer, 1024, f);
if(sigcount % 2 == 0){ //stop printing at odd number of signals received
printf("%d %s", linecount, buffer);
}
linecount++;
}
rewind(f);
linecount = 1;
} while(linecount == 1); //infinite loop
} else if(sigcount < (n + 6)) {
signal(SIGUSR1, SIG_IGN); // ignore signal
} else {
printf("%d signals received", sigcount);
signal(SIGUSR1, sig_exit); // terminate parent process
}
} else {
for(i = 0; i < n; i++){
interval = rand() % 10 + 1; //send signals at random interval
sigcount++; //number of signals sent
printf("Sending signal %d from %d to %d\n", sigcount, getpid(), getppid()); //debugging
kill(getppid(), SIGUSR1); //send signal
printf("Child sleeping for %d seconds\n", interval); //debugging
sleep(interval); //let child sleep after sending signal
}
}
}
/** Checks so command line argument is valid **/
void checkArguments(int nbrOfArg){
int k;
if(nbrOfArg != 2){
printf("Wrong number of arguments");
exit(-1);
}
}
void sig_handler(int signo){
if(signo == SIGUSR1){
printf("Receivied SIGUSR1 signal\n");
} else printf("Error: received undefined signal\n");
}
void sig_exit(int signo){
if(signo == SIGUSR1){
printf("Received SIGUSR1 signal\n");
exit(SIGUSR1);
} else printf("Error: received undefined signal\n");
}发布于 2018-11-12 20:26:03
除了注释中的要点之外,你还应该看看你在哪里设置你的信号处理程序,它是在你调用fork()并启动子进程之后,所以你得到的是父进程和子进程之间的竞争条件:
fork()
/ \
Parent Child
CheckArguments Send signal
Open file
Create Handler因此,子进程在父进程注册其处理程序之前启动并发送信号。您可以通过在发送第一个信号之前在子进程中添加睡眠来检查这是否是问题所在。
此外,信号的行为在不同的Unix版本之间也有所不同,当我在我的机器上尝试您的代码时,第一个信号被捕获,但之后就没有了。这是因为在我的机器上,信号处理程序在运行后被卸载,您需要将其作为信号处理程序的一部分重新启用。
void sig_handler(int signo){
if(signo == SIGUSR1)
{
printf("Receivied SIGUSR1 signal\n");
}
else {
printf("Error: received undefined signal\n");
}
signal(SIGUSR1, sig_handler);
}我也会考虑改用sigaction(而不是signal(),它能让你更好地控制信号处理。
发布于 2018-11-13 03:17:20
使用signal()时,不同系统的行为会有所不同。在运行macOS (10.14.1 Mojave,但它也适用于其他版本)的Mac上,使用signal()的原始代码工作正常-有各种陷阱,但信号处理工作正常。在运行Ubuntu18.04LTS(托管在同一个Mac上)的VM中,使用signal()的代码不能很好地工作,因为(如comments中所指出的),当信号被捕获时,在进入信号处理程序之前,信号处理被重置为默认值。这就设置了一个竞争条件。这两种不同的行为都符合标准C- macOS提供的“可靠信号”,而Linux则没有。
然而,并不是一切都失败了;Linux和macOS都有sigaction(),它允许很好的控制-并且可以用来模拟这两组signal()行为中的任何一组。另请参阅What is the difference between sigaction() and signal()?
还有一些其他问题需要处理。在派生之前,您应该验证参数并(检查是否可以)打开文件;您应该报告错误。子进程应该注意到它的原始父进程的PID,这样如果父进程死了,它可以尝试向它发送一个信号,并被通知失败。当原来的父进程终止时,父PID切换到PID 1,即init进程,这基本上会忽略该信号。
我修复了feof()的问题--没有理由在循环的控制条件下使用feof() (参见while (!feof(file)) is always wrong!)。改为测试基本I/O功能。( Ubuntu上的C库标记了fgets()函数,因此必须使用返回值。注意编译器的警告。)
下面的代码减慢了主打印循环的速度,因此它每秒处理4行代码,而不是全速运行。macOS和Linux都有nanosleep();Linux没有提供usleep(),尽管它有一个更简单(但功能较弱)的接口。
下面的代码将父进程使用的sigcount与子进程用来对接收和发送的信号进行计数的i分开。我还假设了C99或更高版本的支持,并将变量声明移到了使用它们的地方。
正如所写的,代码永远不会进入忽略SIGUSR1的状态,更不用说触发错误的状态了。子进程(现在)发送了足够的信号(n *= 2;发送的信号是父进程“期望”的两倍),但是父进程仍然停留在if (sigcount < n)的原始代码中。请注意,如果忽略接收到的信号,则无法对这些信号进行计数。这部分代码需要认真地重新编写。在接收到适当数量的信号后,您应该退出文件读取循环,然后简单地计算接下来的五个信号(不要忽略它们),然后设置sig_exit处理程序。这在下面的代码中没有实现。
我没有尝试修复在信号处理程序中调用printf()的问题。在这段代码中,它似乎不会造成麻烦(我也不希望它会造成麻烦),但总的来说,它是危险的。有关更多详细信息,请参阅How to avoid using printf() in signal handlers?。
#define _XOPEN_SOURCE 700允许定义POSIX函数等,即使编译选项调用-std=c11 (这会禁用大多数扩展)。程序的源代码是sig41.c,因此程序是sig41。代码用(GCC 8.2.0)编译得很干净:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
> sig41.c -o sig41
$除了一些其他的小改动(例如,在stderr上报告错误),下面的代码可以在Ubuntu和macOS上运行:
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static void sig_exit(int signo);
static void sig_handler(int signo);
static int sigcount, n;
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
FILE *f = fopen(argv[1], "r");
if (f == NULL)
{
fprintf(stderr, "Error opening file %s for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
struct sigaction sa = { 0 };
sa.sa_handler = sig_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, 0);
srand(time(NULL));
n = rand() % 20 + 10;
int pid = fork();
if (pid < 0)
{
fprintf(stderr, "failed to fork!\n");
exit(EXIT_FAILURE);
}
else if (pid != 0)
{
printf("%d\n", getpid());
if (sigcount < n)
{
int linecount = 1;
while (linecount == 1)
{
char buffer[1024];
while (fgets(buffer, 1024, f))
{
if (sigcount % 2 == 0)
{
printf("%d %s", linecount, buffer);
}
linecount++;
// nap time = quarter second
struct timespec nap = { .tv_sec = 0, .tv_nsec = 250000000 };
nanosleep(&nap, NULL);
}
rewind(f);
linecount = 1;
}
}
else if (sigcount < (n + 6))
{
printf("Going into SIG_IGN mode\n");
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR1, &sa, 0);
}
else
{
printf("%d of %d signals received - sig_exit mode\n", sigcount, n);
sa.sa_handler = sig_exit;
sigaction(SIGUSR1, &sa, 0);
}
}
else
{
fclose(f);
int pid = getpid();
int ppid = getppid();
n *= 2; // Child needs to send more signals
for (int i = 0; i < n; i++)
{
int interval = rand() % 10 + 1;
printf("Sending signal %d of %d from %d to %d\n", i + 1, n, pid, ppid);
if (kill(ppid, SIGUSR1) != 0)
{
fprintf(stderr, "Child failed to signal parent - exiting\n");
exit(1);
}
printf("Child sleeping for %d seconds\n", interval);
sleep(interval);
}
}
}
static void sig_handler(int signo)
{
sigcount++;
if (signo == SIGUSR1)
printf("Received SIGUSR1 signal %d of %d\n", sigcount, n);
else
printf("Error: received undefined signal\n");
}
static void sig_exit(int signo)
{
if (signo == SIGUSR1)
{
fprintf(stderr, "Received SIGUSR1 signal\n");
exit(SIGUSR1);
}
else
printf("Error: received undefined signal\n");
}很难很好地展示它的工作原理。我最终中断了程序来停止它。
$ ./sig41 sig41.c
3247
1 #define _XOPEN_SOURCE 700
Sending signal 1 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 1 of 15
Sending signal 2 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 2 of 15
30 sa.sa_flags = 0;
31 sigemptyset(&sa.sa_mask);
…
56 }
57 linecount++;
Sending signal 3 of 30 from 3248 to 3247
Child sleeping for 1 seconds
Received SIGUSR1 signal 3 of 15
Sending signal 4 of 30 from 3248 to 3247
Child sleeping for 4 seconds
Received SIGUSR1 signal 4 of 15
62 rewind(f);
63 linecount = 1;
…
76 sigaction(SIGUSR1, &sa, 0);
77 }
Sending signal 5 of 30 from 3248 to 3247
Child sleeping for 2 seconds
Received SIGUSR1 signal 5 of 15
Sending signal 6 of 30 from 3248 to 3247
Child sleeping for 3 seconds
Received SIGUSR1 signal 6 of 15
86 {
87 int interval = rand() % 10 + 1;
…
96 }
97 }
Sending signal 7 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 7 of 15
Sending signal 8 of 30 from 3248 to 3247
Child sleeping for 10 seconds
Received SIGUSR1 signal 8 of 15
8 static void sig_exit(int signo);
9 static void sig_handler(int signo);
…
46 {
47 int linecount = 1;
Sending signal 9 of 30 from 3248 to 3247
Child sleeping for 5 seconds
Received SIGUSR1 signal 9 of 15
Sending signal 10 of 30 from 3248 to 3247
Child sleeping for 8 seconds
Received SIGUSR1 signal 10 of 15
68 printf("Going into SIG_IGN mode\n");
69 sa.sa_handler = SIG_IGN;
…
98 }
99
Sending signal 11 of 30 from 3248 to 3247
Child sleeping for 9 seconds
Received SIGUSR1 signal 11 of 15
Sending signal 12 of 30 from 3248 to 3247
Child sleeping for 4 seconds
Received SIGUSR1 signal 12 of 15
18 exit(EXIT_FAILURE);
19 }
…
32 sigaction(SIGUSR1, &sa, 0);
33
Sending signal 13 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 13 of 15
Sending signal 14 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 14 of 15
58 // nap time = quarter second
59 struct timespec nap = { .tv_sec = 0, .tv_nsec = 250000000 };
…
80 {
81 fclose(f);
Sending signal 15 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 15 of 15
Sending signal 16 of 30 from 3248 to 3247
Child sleeping for 8 seconds
Received SIGUSR1 signal 16 of 15
110 {
111 if (signo == SIGUSR1)
…
22 if (f == NULL)
23 {
Sending signal 17 of 30 from 3248 to 3247
Child sleeping for 1 seconds
Received SIGUSR1 signal 17 of 15
Sending signal 18 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 18 of 15
28 struct sigaction sa = { 0 };
29 sa.sa_handler = sig_handler;
…
^C
$如果您处理完输出,您可以看到输出暂停了给定的行数-如果孩子休眠1秒,就会省略4行输出。
https://stackoverflow.com/questions/53260555
复制相似问题