如何不断从父进程反复向子进程发送信号?
How to keep sending signals to children from parent process repeatedly?
我试图让一个程序创建 n 个子进程来读取一个文件,而父进程不断循环它们并尝试发送信号以在该进程空闲时继续读取文件。否则,如果该进程被占用,它将移动到下一个进程。这是我使用信号的方法,但输出是意外的。如果我使用 printf 调试父级 while 循环中的代码,我只会得到 printf 语句本身。为什么会这样?
据我了解,预期的输出应该是所有 n 个子进程几乎同时从文件中读取(文件很小),然后在再次发生这种情况之前间隔 1 秒。这是正确的,还是由于竞争条件而不确定?
我知道使用管道可能有更好的方法,但是使用信号可以吗?提前致谢。
我知道 signal() 不像 sigaction() 那样被广泛使用,正如手册页中提到的那样,但这仍然可以解决吗?
printf的意外输出会不会是printf本身干扰了IPC?
编辑:Pastebin Link 用于测试:https://pastebin.com/ZWFnkJDA
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;
//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int signo, siginfo_t* info, void* context);
static void sig_usr1(int signo)
{
//Now the child process waits for reading the Filename
//Block SIGUSR1 until it's complete
signal(SIGUSR1, SIG_IGN);
printf("Blocked now.\n");
printf("Child no %d is reading now.\n\n",getpid());
fd = open(filename, O_RDONLY | O_CREAT);
char buf = 'a';
int k=0;
char* op = (char*) malloc (255*sizeof(char));
while(read (fd, &buf, 1))
{
if (buf == '\n')
{
op[k] = '[=10=]';
break;
}
else
{
op[k++] = buf;
}
}
//Now wait for a second and then send a signal
sleep(1);
//Print the contents of the buffer via op
printf("Output: %s\n\n", op);
//Now unblock the signal
kill(getppid(), SIGUSR2);
signal(SIGUSR1, sig_usr1);
printf("Unblocked now\n");
}
static void sig_usr2(int signo, siginfo_t* info, void* context)
{
if (signo == SIGUSR2)
{
child_pid = info->si_pid;
printf("Parent Received SIGUSR2. Child Process with PID %d is now free\n\n", child_pid);
}
}
int main(int argc, char* argv[])
{
//Filename is the first argument
filename = argv[1];
//Number of Child Processes to be spawned
no_of_children = atoi(argv[2]);
cpid = (int*) malloc (no_of_children*sizeof(int));
//Create a sigaction() handler for SIGUSR2
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sig_usr2;
sigaction(SIGUSR2, &sa, NULL);
//Create no_of_children children
for(int i=0; i<no_of_children; i++)
{
cpid[i] = fork();
if (cpid[i] == 0)
{
//Inside a child
printf("Created %dth child process", i);
printf(" with Process ID = %d\n", getpid());
signal(SIGUSR1, sig_usr1);
while(1)
{
pause();
}
//Every child process must exit so control goes back to the parent
exit(0);
}
}
//Returns to the parent process
while(1)
{
int fpid = cpid[j];
//Send the signal to the free child process
//printf("Sending to PID %d\n", fpid); //----> Uncommenting this line only prints this statement. Why does this happen?
kill(fpid, SIGUSR1);
j = (j < no_of_children - 1) ? j + 1 : 0; //----->Does not work as expected if i uncomment this line. I want to switch between PIDs and send signals to all of them
}
return 0;
}
- 示例输出:
$ ./a.out sample.txt 4
Created 0th child process with Process ID = 15734
Created 1th child process with Process ID = 15735
PID: 15737 -> PID: 15736 -> PID: 15735 -> PID: 15734
Created 2th child process with Process ID = 15736
Blocked now.
Child no 15734 is reading now.
Created 3th child process with Process ID = 15737
Output: This is a sample file
Unblocked now
Parent Received SIGUSR2. Child Process with PID 15734 is now free
Blocked now.
Child no 15734 is reading now.
Blocked now.
Child no 15735 is reading now.
Output: This is a sample file
Unblocked now
Output: This is a sample file
Parent Received SIGUSR2. Child Process with PID 15735 is now free
Blocked now.
Child no 15734 is reading now.
Unblocked now
Parent Received SIGUSR2. Child Process with PID 15736 is now free
Blocked now.
Child no 15735 is reading now.
Blocked now.
Blocked now.
Child no 15736 is reading now.
Child no 15737 is reading now.
^\%
注意:这不是答案,只是一些经过编辑的代码和可能导致解决方案的结论。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;
int my_pid;
//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int);
static void sig_usr1(int signo)
{
//Now the child process waits for reading the Filename
//Block SIGUSR1 until it's complete
signal(SIGUSR1, SIG_IGN);
printf("%d\tBlocked now.\n", my_pid);
printf("%d\tChild no %d is reading now.\n",my_pid, getpid());
fd = open(filename, O_RDONLY | O_CREAT);
char buf = 'a';
int k=0;
char* op = (char*) malloc (255*sizeof(char));
while(read (fd, &buf, 1))
{
if (buf == '\n')
{
op[k] = '[=10=]';
break;
}
else
{
op[k++] = buf;
}
}
//Now wait for a second and then send a signal
sleep(1);
//Print the contents of the buffer via op
printf("%d\tOutput: %s\n", my_pid, op);
//Now unblock the signal
kill(getppid(), SIGUSR2);
signal(SIGUSR1, sig_usr1);
printf("%d\tUnblocked now\n", my_pid);
}
static void sig_usr2(int signo)
{
if (signo == SIGUSR2)
{
printf("%d\tParent Received SIGUSR2. Child Process with PID %d is now free\n", my_pid, cpid[j]);
kill (cpid[j++], SIGUSR1);
if (j == no_of_children)
{
j = 0;
}
}
}
int main(int argc, char* argv[])
{
my_pid = getpid();
//Filename is the first argument
filename = argv[1];
//Number of Child Processes to be spawned
no_of_children = atoi(argv[2]);
cpid = (int*) malloc (no_of_children*sizeof(int));
signal(SIGUSR2, sig_usr2);
//Create no_of_children children
for(int i=0; i<no_of_children; i++)
{
cpid[i] = fork();
if (cpid[i] == 0)
{
//Inside a child
my_pid = getpid();
printf("%d\tCreated %dth child process", my_pid, i);
printf(" with Process ID = %d\n", getpid());
signal(SIGUSR1, sig_usr1);
while(1)
{
pause();
}
//Every child process must exit so control goes back to the parent
exit(0);
}
printf("%d\tforked %dth child -> %d\n", my_pid, i, cpid[i]);
}
//Returns to the parent process
while(1)
{
int fpid = cpid[j];
//Send the signal to the free child process
printf("%d\tSending to PID %d\n", my_pid, fpid);
kill(fpid, SIGUSR1);
//j = (j < no_of_children - 1) ? j + 1 : 0;
pause();
}
return 0;
}
好的,我做了什么?
- 在每个
printf()
的开头加上当前进程的PID。为此,变量 my_pid
被添加并设置在方便的地方。是的,我知道一些输出已经提供了这些信息。我不想更改太多 OP 的源代码。
- 删除了课程中的双重
\n
。他们没有帮助。
- 要查看
fork()
是否成功,请打印其结果。
- 最后在主进程的死循环中添加一个
printf
。我想看看哪个进程以何种速率收到了多少次信号。哇,好多啊!
嗯,会不会是主进程用太多的信号淹没了子进程,以至于它甚至无法调用 signal()
来暂时阻止它?
- 最后在主进程的死循环中加入
pause()
现在事情开始奏效了,但很可能不是 OP 想要的方式。无论如何,所有子进程都被创建并接收它们的信号、读取文件、发回它们的信号等等。
看来系统信号太多了。必须考虑这样的算法。所以这是我的建议:
- 放慢速度。以后可以加快速度。
- 在代码中放置尽可能多的观察点(此处:
printf()
)以跟踪所有事件。
- 特别是对于多处理,考虑一种可视化结果的方法。我在编辑器中复制了 shell 输出并缩进了这样的行:
$ ./signal_repetition signal_repetition.c 4
1901 forked 0th child -> 1902
1902 Created 0th child process with Process ID = 1902
1901 forked 1th child -> 1903
1903 Created 1th child process with Process ID = 1903
1901 forked 2th child -> 1904
1904 Created 2th child process with Process ID = 1904
1901 forked 3th child -> 1905
1901 Sending to PID 1902
1902 Blocked now.
1902 Child no 1902 is reading now.
1905 Created 3th child process with Process ID = 1905
1902 Output: #include<stdio.h>
1902 Unblocked now
1901 Parent Received SIGUSR2. Child Process with PID 1902 is now free
1901 Sending to PID 1903
1902 Blocked now.
1902 Child no 1902 is reading now.
1903 Blocked now.
1903 Child no 1903 is reading now.
1902 Output: #include<stdio.h>
1902 Unblocked now
1901 Parent Received SIGUSR2. Child Process with PID 1903 is now free
1901 Sending to PID 1904
1904 Blocked now.
1903 Output: #include<stdio.h>
1904 Child no 1904 is reading now.
1903 Unblocked now
1903 Blocked now.
1903 Child no 1903 is reading now.
1901 Parent Received SIGUSR2. Child Process with PID 1904 is now free
1901 Sending to PID 1905
1905 Blocked now.
1905 Child no 1905 is reading now.
1903 Output: #include<stdio.h>
1904 Output: #include<stdio.h>
1903 Unblocked now
1904 Unblocked now
1904 Blocked now.
1904 Child no 1904 is reading now.
1901 Parent Received SIGUSR2. Child Process with PID 1905 is now free
- 您可以在每个进程自己的日志文件中创建一个日志。添加分辨率至少为毫秒的时间戳,以便稍后同步所有日志。这样您就可以避免
stdout
. 上的混合输出问题
还有很多其他事情需要解决。玩得很开心!祝你好运!
我试图让一个程序创建 n 个子进程来读取一个文件,而父进程不断循环它们并尝试发送信号以在该进程空闲时继续读取文件。否则,如果该进程被占用,它将移动到下一个进程。这是我使用信号的方法,但输出是意外的。如果我使用 printf 调试父级 while 循环中的代码,我只会得到 printf 语句本身。为什么会这样?
据我了解,预期的输出应该是所有 n 个子进程几乎同时从文件中读取(文件很小),然后在再次发生这种情况之前间隔 1 秒。这是正确的,还是由于竞争条件而不确定?
我知道使用管道可能有更好的方法,但是使用信号可以吗?提前致谢。
我知道 signal() 不像 sigaction() 那样被广泛使用,正如手册页中提到的那样,但这仍然可以解决吗?
printf的意外输出会不会是printf本身干扰了IPC?
编辑:Pastebin Link 用于测试:https://pastebin.com/ZWFnkJDA
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;
//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int signo, siginfo_t* info, void* context);
static void sig_usr1(int signo)
{
//Now the child process waits for reading the Filename
//Block SIGUSR1 until it's complete
signal(SIGUSR1, SIG_IGN);
printf("Blocked now.\n");
printf("Child no %d is reading now.\n\n",getpid());
fd = open(filename, O_RDONLY | O_CREAT);
char buf = 'a';
int k=0;
char* op = (char*) malloc (255*sizeof(char));
while(read (fd, &buf, 1))
{
if (buf == '\n')
{
op[k] = '[=10=]';
break;
}
else
{
op[k++] = buf;
}
}
//Now wait for a second and then send a signal
sleep(1);
//Print the contents of the buffer via op
printf("Output: %s\n\n", op);
//Now unblock the signal
kill(getppid(), SIGUSR2);
signal(SIGUSR1, sig_usr1);
printf("Unblocked now\n");
}
static void sig_usr2(int signo, siginfo_t* info, void* context)
{
if (signo == SIGUSR2)
{
child_pid = info->si_pid;
printf("Parent Received SIGUSR2. Child Process with PID %d is now free\n\n", child_pid);
}
}
int main(int argc, char* argv[])
{
//Filename is the first argument
filename = argv[1];
//Number of Child Processes to be spawned
no_of_children = atoi(argv[2]);
cpid = (int*) malloc (no_of_children*sizeof(int));
//Create a sigaction() handler for SIGUSR2
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sig_usr2;
sigaction(SIGUSR2, &sa, NULL);
//Create no_of_children children
for(int i=0; i<no_of_children; i++)
{
cpid[i] = fork();
if (cpid[i] == 0)
{
//Inside a child
printf("Created %dth child process", i);
printf(" with Process ID = %d\n", getpid());
signal(SIGUSR1, sig_usr1);
while(1)
{
pause();
}
//Every child process must exit so control goes back to the parent
exit(0);
}
}
//Returns to the parent process
while(1)
{
int fpid = cpid[j];
//Send the signal to the free child process
//printf("Sending to PID %d\n", fpid); //----> Uncommenting this line only prints this statement. Why does this happen?
kill(fpid, SIGUSR1);
j = (j < no_of_children - 1) ? j + 1 : 0; //----->Does not work as expected if i uncomment this line. I want to switch between PIDs and send signals to all of them
}
return 0;
}
- 示例输出:
$ ./a.out sample.txt 4
Created 0th child process with Process ID = 15734
Created 1th child process with Process ID = 15735
PID: 15737 -> PID: 15736 -> PID: 15735 -> PID: 15734
Created 2th child process with Process ID = 15736
Blocked now.
Child no 15734 is reading now.
Created 3th child process with Process ID = 15737
Output: This is a sample file
Unblocked now
Parent Received SIGUSR2. Child Process with PID 15734 is now free
Blocked now.
Child no 15734 is reading now.
Blocked now.
Child no 15735 is reading now.
Output: This is a sample file
Unblocked now
Output: This is a sample file
Parent Received SIGUSR2. Child Process with PID 15735 is now free
Blocked now.
Child no 15734 is reading now.
Unblocked now
Parent Received SIGUSR2. Child Process with PID 15736 is now free
Blocked now.
Child no 15735 is reading now.
Blocked now.
Blocked now.
Child no 15736 is reading now.
Child no 15737 is reading now.
^\%
注意:这不是答案,只是一些经过编辑的代码和可能导致解决方案的结论。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;
int my_pid;
//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int);
static void sig_usr1(int signo)
{
//Now the child process waits for reading the Filename
//Block SIGUSR1 until it's complete
signal(SIGUSR1, SIG_IGN);
printf("%d\tBlocked now.\n", my_pid);
printf("%d\tChild no %d is reading now.\n",my_pid, getpid());
fd = open(filename, O_RDONLY | O_CREAT);
char buf = 'a';
int k=0;
char* op = (char*) malloc (255*sizeof(char));
while(read (fd, &buf, 1))
{
if (buf == '\n')
{
op[k] = '[=10=]';
break;
}
else
{
op[k++] = buf;
}
}
//Now wait for a second and then send a signal
sleep(1);
//Print the contents of the buffer via op
printf("%d\tOutput: %s\n", my_pid, op);
//Now unblock the signal
kill(getppid(), SIGUSR2);
signal(SIGUSR1, sig_usr1);
printf("%d\tUnblocked now\n", my_pid);
}
static void sig_usr2(int signo)
{
if (signo == SIGUSR2)
{
printf("%d\tParent Received SIGUSR2. Child Process with PID %d is now free\n", my_pid, cpid[j]);
kill (cpid[j++], SIGUSR1);
if (j == no_of_children)
{
j = 0;
}
}
}
int main(int argc, char* argv[])
{
my_pid = getpid();
//Filename is the first argument
filename = argv[1];
//Number of Child Processes to be spawned
no_of_children = atoi(argv[2]);
cpid = (int*) malloc (no_of_children*sizeof(int));
signal(SIGUSR2, sig_usr2);
//Create no_of_children children
for(int i=0; i<no_of_children; i++)
{
cpid[i] = fork();
if (cpid[i] == 0)
{
//Inside a child
my_pid = getpid();
printf("%d\tCreated %dth child process", my_pid, i);
printf(" with Process ID = %d\n", getpid());
signal(SIGUSR1, sig_usr1);
while(1)
{
pause();
}
//Every child process must exit so control goes back to the parent
exit(0);
}
printf("%d\tforked %dth child -> %d\n", my_pid, i, cpid[i]);
}
//Returns to the parent process
while(1)
{
int fpid = cpid[j];
//Send the signal to the free child process
printf("%d\tSending to PID %d\n", my_pid, fpid);
kill(fpid, SIGUSR1);
//j = (j < no_of_children - 1) ? j + 1 : 0;
pause();
}
return 0;
}
好的,我做了什么?
- 在每个
printf()
的开头加上当前进程的PID。为此,变量my_pid
被添加并设置在方便的地方。是的,我知道一些输出已经提供了这些信息。我不想更改太多 OP 的源代码。 - 删除了课程中的双重
\n
。他们没有帮助。 - 要查看
fork()
是否成功,请打印其结果。 - 最后在主进程的死循环中添加一个
printf
。我想看看哪个进程以何种速率收到了多少次信号。哇,好多啊!
嗯,会不会是主进程用太多的信号淹没了子进程,以至于它甚至无法调用 signal()
来暂时阻止它?
- 最后在主进程的死循环中加入
pause()
现在事情开始奏效了,但很可能不是 OP 想要的方式。无论如何,所有子进程都被创建并接收它们的信号、读取文件、发回它们的信号等等。
看来系统信号太多了。必须考虑这样的算法。所以这是我的建议:
- 放慢速度。以后可以加快速度。
- 在代码中放置尽可能多的观察点(此处:
printf()
)以跟踪所有事件。 - 特别是对于多处理,考虑一种可视化结果的方法。我在编辑器中复制了 shell 输出并缩进了这样的行:
$ ./signal_repetition signal_repetition.c 4
1901 forked 0th child -> 1902
1902 Created 0th child process with Process ID = 1902
1901 forked 1th child -> 1903
1903 Created 1th child process with Process ID = 1903
1901 forked 2th child -> 1904
1904 Created 2th child process with Process ID = 1904
1901 forked 3th child -> 1905
1901 Sending to PID 1902
1902 Blocked now.
1902 Child no 1902 is reading now.
1905 Created 3th child process with Process ID = 1905
1902 Output: #include<stdio.h>
1902 Unblocked now
1901 Parent Received SIGUSR2. Child Process with PID 1902 is now free
1901 Sending to PID 1903
1902 Blocked now.
1902 Child no 1902 is reading now.
1903 Blocked now.
1903 Child no 1903 is reading now.
1902 Output: #include<stdio.h>
1902 Unblocked now
1901 Parent Received SIGUSR2. Child Process with PID 1903 is now free
1901 Sending to PID 1904
1904 Blocked now.
1903 Output: #include<stdio.h>
1904 Child no 1904 is reading now.
1903 Unblocked now
1903 Blocked now.
1903 Child no 1903 is reading now.
1901 Parent Received SIGUSR2. Child Process with PID 1904 is now free
1901 Sending to PID 1905
1905 Blocked now.
1905 Child no 1905 is reading now.
1903 Output: #include<stdio.h>
1904 Output: #include<stdio.h>
1903 Unblocked now
1904 Unblocked now
1904 Blocked now.
1904 Child no 1904 is reading now.
1901 Parent Received SIGUSR2. Child Process with PID 1905 is now free
- 您可以在每个进程自己的日志文件中创建一个日志。添加分辨率至少为毫秒的时间戳,以便稍后同步所有日志。这样您就可以避免
stdout
. 上的混合输出问题
还有很多其他事情需要解决。玩得很开心!祝你好运!