C,使用信号使父子进程正常工作

C, getting a parent and child process to work using signals

我正试图让我的子进程生成一个介于1 -9之间的随机数,然后将其发送给我的父进程,每次按下控件Z时,它将显示在屏幕上。我还使用dup2()将流从printf和scanf更改为每个进程的管道的读取和写入端。

该代码的工作原理如下,按C键启动主程序。现在,每次从子级发送一个随机数并将其显示在父级时,它都等待按下控件Z。我面临的问题是,似乎再次按下控件Z时,子级只运行一次,它仅运行父代代码。管道也只会显示相同的编号,并且永远不会更改,因此不会被更新。我还注意到,如果我在dup函数printf(" a \\\\
"),它会打印出随机数。...但是,子运行一次的问题仍然存在。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>

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 \
"
);
    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 \
"
);
          exit(1);
        }

      if (secondSel == 1)
        {
          fork1 = fork();
        }


      if (fork1 < 0)
        {
          printf("Error with first fork. \
"
);
          exit(1);
        }
      else if (fork1 == 0) //first child process
        {
          signal(SIGTSTP, handleSignal);
          pause();
          printf("a \
"
);
          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 \
"
);
          int f;
          close(fd[1]);
          dup2(fd[0], 0);
          scanf("%d \
"
, &f);
          printf("%d \
"
, f);
          close(fd[0]);
        }
      secondSel = 0;
    }
    }

}


注释/代码:

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

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

在无限循环内,代码:

1
2
3
4
5
6
7
8
9
10
        if (secondSel == 1)
        {  
            pipe1 = pipe(fd);
        }  
        if (pipe1 < 0)
        {  
            printf("Error creating pipe 1 \
"
);
            exit(1);
        }

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

1
2
3
4
5
6
7
8
9
        if (secondSel == 1)
        {  
            if (pipe(fd) < 0)
            {
                fprintf(stderr,"Error creating pipe 1\
"
);
                exit(1);
            }
        }

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

您的代码在第一个循环后会仔细关闭管道,但永远不要重新打开它。最终,这就是第二次和后续迭代失败的原因。如果您不尝试在每个周期中做所有事情,那么您的代码会更好。另外,使用标准输出来调试信息会遇到各种问题。最好改用标准错误-尤其是在标准输出不能正常工作的情况下。

插装代码

这是代码的检测版本。 err_syserr()函数几乎是通用的。使用usleep()的条件是该代码所特有的,并确保终止的错误消息的输出正常排序。我对可能失败的函数调用进行了测试-第一个关闭在第二个周期失败,因为管道描述符在第一个周期结束时全部关闭。 (请注意,在fork()之后重用pipe()无济于事-父级和子级中的管道不会相互连接。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

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\
"
);
        exit(1);
    }
    //printf("Waiting for interrupt\
");

    //pause();

    while (1)
    {
        fprintf(stderr,"Looping: %d (%d)\
"
, (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)\
"
, pipe1, fd[0], fd[1]);
            }
            if (pipe1 < 0)
            {
                printf("Error creating pipe 1\
"
);
                exit(1);
            }

            if (secondSel == 1)
            {
                fork1 = fork();
                fprintf(stderr,"Forked: %d (%d, %d)\
"
, fork1, (int)getpid(), (int)getppid());
            }

            if (fork1 < 0)
            {
                printf("Error with first fork.\
"
);
                exit(1);
            }
            else if (fork1 == 0) // first child process
            {
                signal(SIGTSTP, handleSignal);
                fprintf(stderr,"Pausing C: %d\
"
, (int)getpid());
                pause();
                fprintf(stderr,"Unpaused C: %d\
"
, (int)getpid());
                printf("a\
"
);
                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\
"
, randNum1);
                if (printf("%d\
"
, randNum1) < 2)
                {
                    fprintf(stderr,"Print C: failed\
"
);
                    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\
"
, (int)getpid());
                pause();
                fprintf(stderr,"Unpaused P: %d\
"
, (int)getpid());
                printf("b\
"
);
                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\
"
);
                    clearerr(stdin);
                }
                printf("Parent: %d\
"
, f);
                if (close(fd[0]) < 0)
                    err_syserr("close(fd[0]=%d) failed", fd[0]);
            }
            secondSel = 0;
        }
    }
    return 0;
}

#include <errno.h>
#include <string.h>
#include <stdarg.h>

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('\
'
, stderr);
    exit(EXIT_FAILURE);
}

示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ ./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;的Stubbing(可以省略)。开始时它不会像疯了一样循环,因为我在等待中断的地方放了一个停顿。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

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\
"
);
        exit(1);
    }

    printf("Waiting for interrupt\
"
);
    pause();
    signal(SIGINT, SIG_IGN);

    pipe1 = pipe(fd);
    if (pipe1 < 0)
    {
        fprintf(stderr,"Error creating pipe 1\
"
);
        exit(1);
    }
    fprintf(stderr,"Created pipe: %d (%d, %d)\
"
, pipe1, fd[0], fd[1]);

    fork1 = fork();
    if (fork1 < 0)
    {
        fprintf(stderr,"Error with fork.\
"
);
        exit(1);
    }

    fprintf(stderr,"Forked: %d (%d, %d)\
"
, 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\
"
, (int)getpid());
            pause();
            fprintf(stderr,"Unpaused C: %d\
"
, (int)getpid());
            int randNum1 = rand() % 9 + 1;
            fprintf(stderr,"Print C: %d\
"
, randNum1);
            if (printf("%d\
"
, randNum1) < 2)
            {
                fprintf(stderr,"Print C: failed\
"
);
                clearerr(stdout);
            }
            fflush(stdout);
        }
    }
    else
    {
        dup2(fd[0], 0);
        close(fd[0]);
        close(fd[1]);
        while (1)
        {
            fprintf(stderr,"Pausing P: %d\
"
, (int)getpid());
            pause();
            fprintf(stderr,"Unpaused P: %d\
"
, (int)getpid());
            int f = 99;
            if (scanf("%d", &f) != 1)
            {
                fprintf(stderr,"Scanf P: failed\
"
);
                clearerr(stdin);
            }
            else
                printf("Parent: %d\
"
, f);
        }
    }

    return 0;
}

示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$  ./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终止了程序。


您遇到的问题是由于第一个数字从子级发送到父级后关闭了管道。

但是,主要问题是您的代码杂乱无章,这使得弄清楚正在发生的事情变得更加困难,并且增加了出错的机会。建议不要像下面那样重组所有代码,而不是将所有内容都放入一个大循环中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.