如何实现生产者消费者使用流程?
How to implement producer-consumer using processes?
我正在尝试使用 1 个父进程和 1 个子进程来实现生产者-消费者应用程序。该程序应该像这样工作:
1 - 父进程是生产者,子进程是消费者。
2 - 生产者创建文件,消费者删除文件。
3 - 创建文件后,父进程向子进程发送一个 SIGUSR1 信号,然后子进程删除该文件并向父进程发送一个 SIGUSR2 信号,向其发出可以再次创建文件的信号。
我已经尝试解决这个问题,但我一直收到这个错误:
User defined signal 1: 30.
我真的不明白可能是什么问题。我刚刚开始学习过程和信号,也许我遗漏了一些东西。任何帮助,将不胜感激。这是我的实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
pid_t child, parent;
void producer()
{
system("touch file");
printf("File was created.\n");
}
void consumer()
{
system("rm file");
printf("File was deleted.\n");
kill(parent, SIGUSR2); // signal -> file can created by parent
}
int main(void)
{
system("touch file");
pid_t pid = fork();
for(int i = 0; i < 10; ++i)
{
if(pid < 0) // error fork()
{
perror("fork()");
return -1;
}
else if(pid == 0) // child proces - consumer
{
child = getpid();
signal(SIGUSR1, consumer);
pause();
}
else // parent process - producer
{
parent = getpid();
signal(SIGUSR2, producer);
// signal -> file can be deleted by child
kill(child, SIGUSR1);
}
}
return 0;
}
编辑:我忘了说一次只能有一个文件。
...如有任何帮助,我们将不胜感激。
关于错误:User defined signal 1: 30,执行速度可能会引发竞争条件,导致在注册处理函数之前终止。请记住,每个 signal
都有一个默认配置(或操作)。对于 SIGUSR1
和 SIGUSR2S
,配置是 term
,(来自下面链接的 signal(7) 页面中的 table)
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
(注意 SIGUSR1
列出的值 30 与您引用的退出条件匹配。)
这意味着您的处理程序函数在第一次遇到 SIGUSR1
之前尚未注册,导致默认操作终止您的应用程序并抛出 signal
相关错误。
同步和计时之间的关系是值得关注的。我发现了一些关于同步的东西,并在下面链接了一个。
时间可以通过适当的同步方法隐式解决,从而不需要任何显式执行流程控制功能。但是,如果需要帮助,请尝试使用 sleep family of functions.
这里有一些其他的一般性建议:
1) printf(和系列)真的不应该在信号处理程序中使用。
2)但是,如果使用,换行符( \n
)是一个好主意(你有),或者使用 fflush 强制写入。
3) 添加一个strace() call来检查是否有任何系统调用流量发生。
Take a look at the signal(7) page.。 (这是很多信息,但暗示了为什么首先在信号处理程序中使用 printf 或 fprintf 可能不是一个好主意。)
Another collection of detailed information on Signal Handling。
除了@ryyker 提到的,另一个问题是,当您的 parent 进程尝试使用全局变量 child
向 child 发送信号时,child没有机会 运行 并收集 pid。所以 parent 将向垃圾 pid 发送信号。更好的方法是在 parent 中使用 pid
变量,在 child 中使用 getppid()
变量。这是似乎提供所需输出的代码
void producer()
{
system("touch file");
printf("File was created.\n");
}
void consumer()
{
system("rm file");
printf("File was deleted.\n");
kill(getppid(), SIGUSR2); // signal -> file can created by parent
}
int main(void)
{
system("touch file");
pid_t pid = fork();
if(pid < 0) // error fork()
{
perror("fork()");
return -1;
}
if(pid > 0) { //parent
signal(SIGUSR2, producer);
}
else { //child
signal(SIGUSR1, consumer);
}
for(int i = 0; i < 10; ++i)
{
if(pid == 0) {// child proces - consumer
pause();
}
else // parent process - producer
{
printf("Iter %d\n",i);
kill(pid, SIGUSR1);
pause();
}
}
return 0;
}
尝试在 C++ 中使用信号量而不是信号。
信号在 OS 中真正用于特殊用途,而信号量用于进程同步。
Posix c++中的命名信号量可以跨进程使用。
以下伪代码会有所帮助。
Semaphore Full,Empty;
------
Producer() //producer
{
waitfor(Empty);//wait for an empty slot
system("touch file");
printf("File was created.\n");
Signal(Full); //Signal one slot is full
}
Consumer() //Consumer
{
WaitFor(Full); //wait for producer to produce
system("rm file");
printf("File was deleted.\n");
Signal(Empty);//Signal that it has consumed, so one empty slot created
}
经过大量研究和阅读所有建议后,我终于设法使该程序正常运行。这是我的实现。如果您发现任何错误或者可能可以做得更好,请随时更正我的代码。我乐于接受建议。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signal_number)
{
sigset_t mask;
if(sigemptyset(&mask) == -1 || sigfillset(&mask) == -1)
{// initialize signal set || block all signals
perror("Failed to initialize the signal mask.");
return;
}
switch(signal_number)
{
case SIGUSR1:
{
if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
{ // entering critical zone
perror("sigprocmask(1)");
return;
} //---------------------
sleep(1);
system("rm file"); /* critical zone */
puts("File was removed.");
//--------------------
if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{// exiting critical zone
perror("1 : sigprocmask()");
return;
}
break;
}
case SIGUSR2:
{
if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
{// entering critical zone
perror("2 : sigprocmask()");
return;
} //---------------------
sleep(1);
system("touch file");
puts("File was created."); /* critical zone */
// --------------------
if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{// exiting critical zone
perror("sigprocmask(2)");
return;
}
break;
}
}
}
int main(void)
{
pid_t pid = fork();
struct sigaction sa;
sa.sa_handler = &signal_handler; // handler function
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
if(pid < 0)
{
perror("fork()");
return -1;
}
for(int i = 0; i < 10; ++i)
{
if(pid > 0) // parent - producer
{
sleep(2);
// signal -> file was created
kill(pid, SIGUSR1);
pause();
}
else // child - consumer
{
pause();
// signal -> file was removed
kill(getppid(), SIGUSR2);
}
}
return 0;
}
我正在尝试使用 1 个父进程和 1 个子进程来实现生产者-消费者应用程序。该程序应该像这样工作:
1 - 父进程是生产者,子进程是消费者。
2 - 生产者创建文件,消费者删除文件。
3 - 创建文件后,父进程向子进程发送一个 SIGUSR1 信号,然后子进程删除该文件并向父进程发送一个 SIGUSR2 信号,向其发出可以再次创建文件的信号。
我已经尝试解决这个问题,但我一直收到这个错误:
User defined signal 1: 30.
我真的不明白可能是什么问题。我刚刚开始学习过程和信号,也许我遗漏了一些东西。任何帮助,将不胜感激。这是我的实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
pid_t child, parent;
void producer()
{
system("touch file");
printf("File was created.\n");
}
void consumer()
{
system("rm file");
printf("File was deleted.\n");
kill(parent, SIGUSR2); // signal -> file can created by parent
}
int main(void)
{
system("touch file");
pid_t pid = fork();
for(int i = 0; i < 10; ++i)
{
if(pid < 0) // error fork()
{
perror("fork()");
return -1;
}
else if(pid == 0) // child proces - consumer
{
child = getpid();
signal(SIGUSR1, consumer);
pause();
}
else // parent process - producer
{
parent = getpid();
signal(SIGUSR2, producer);
// signal -> file can be deleted by child
kill(child, SIGUSR1);
}
}
return 0;
}
编辑:我忘了说一次只能有一个文件。
...如有任何帮助,我们将不胜感激。
关于错误:User defined signal 1: 30,执行速度可能会引发竞争条件,导致在注册处理函数之前终止。请记住,每个 signal
都有一个默认配置(或操作)。对于 SIGUSR1
和 SIGUSR2S
,配置是 term
,(来自下面链接的 signal(7) 页面中的 table)
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
(注意 SIGUSR1
列出的值 30 与您引用的退出条件匹配。)
这意味着您的处理程序函数在第一次遇到 SIGUSR1
之前尚未注册,导致默认操作终止您的应用程序并抛出 signal
相关错误。
同步和计时之间的关系是值得关注的。我发现了一些关于同步的东西,并在下面链接了一个。
时间可以通过适当的同步方法隐式解决,从而不需要任何显式执行流程控制功能。但是,如果需要帮助,请尝试使用 sleep family of functions.
这里有一些其他的一般性建议:
1) printf(和系列)真的不应该在信号处理程序中使用。
2)但是,如果使用,换行符( \n
)是一个好主意(你有),或者使用 fflush 强制写入。
3) 添加一个strace() call来检查是否有任何系统调用流量发生。
Take a look at the signal(7) page.。 (这是很多信息,但暗示了为什么首先在信号处理程序中使用 printf 或 fprintf 可能不是一个好主意。)
Another collection of detailed information on Signal Handling。
除了@ryyker 提到的,另一个问题是,当您的 parent 进程尝试使用全局变量 child
向 child 发送信号时,child没有机会 运行 并收集 pid。所以 parent 将向垃圾 pid 发送信号。更好的方法是在 parent 中使用 pid
变量,在 child 中使用 getppid()
变量。这是似乎提供所需输出的代码
void producer()
{
system("touch file");
printf("File was created.\n");
}
void consumer()
{
system("rm file");
printf("File was deleted.\n");
kill(getppid(), SIGUSR2); // signal -> file can created by parent
}
int main(void)
{
system("touch file");
pid_t pid = fork();
if(pid < 0) // error fork()
{
perror("fork()");
return -1;
}
if(pid > 0) { //parent
signal(SIGUSR2, producer);
}
else { //child
signal(SIGUSR1, consumer);
}
for(int i = 0; i < 10; ++i)
{
if(pid == 0) {// child proces - consumer
pause();
}
else // parent process - producer
{
printf("Iter %d\n",i);
kill(pid, SIGUSR1);
pause();
}
}
return 0;
}
尝试在 C++ 中使用信号量而不是信号。 信号在 OS 中真正用于特殊用途,而信号量用于进程同步。
Posix c++中的命名信号量可以跨进程使用。
以下伪代码会有所帮助。
Semaphore Full,Empty;
------
Producer() //producer
{
waitfor(Empty);//wait for an empty slot
system("touch file");
printf("File was created.\n");
Signal(Full); //Signal one slot is full
}
Consumer() //Consumer
{
WaitFor(Full); //wait for producer to produce
system("rm file");
printf("File was deleted.\n");
Signal(Empty);//Signal that it has consumed, so one empty slot created
}
经过大量研究和阅读所有建议后,我终于设法使该程序正常运行。这是我的实现。如果您发现任何错误或者可能可以做得更好,请随时更正我的代码。我乐于接受建议。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signal_number)
{
sigset_t mask;
if(sigemptyset(&mask) == -1 || sigfillset(&mask) == -1)
{// initialize signal set || block all signals
perror("Failed to initialize the signal mask.");
return;
}
switch(signal_number)
{
case SIGUSR1:
{
if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
{ // entering critical zone
perror("sigprocmask(1)");
return;
} //---------------------
sleep(1);
system("rm file"); /* critical zone */
puts("File was removed.");
//--------------------
if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{// exiting critical zone
perror("1 : sigprocmask()");
return;
}
break;
}
case SIGUSR2:
{
if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
{// entering critical zone
perror("2 : sigprocmask()");
return;
} //---------------------
sleep(1);
system("touch file");
puts("File was created."); /* critical zone */
// --------------------
if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{// exiting critical zone
perror("sigprocmask(2)");
return;
}
break;
}
}
}
int main(void)
{
pid_t pid = fork();
struct sigaction sa;
sa.sa_handler = &signal_handler; // handler function
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
if(pid < 0)
{
perror("fork()");
return -1;
}
for(int i = 0; i < 10; ++i)
{
if(pid > 0) // parent - producer
{
sleep(2);
// signal -> file was created
kill(pid, SIGUSR1);
pause();
}
else // child - consumer
{
pause();
// signal -> file was removed
kill(getppid(), SIGUSR2);
}
}
return 0;
}