我正尝试实施此操作:
制作一个执行以下操作的C多进程程序:
进程P生成两个子进程P1和P2。两个儿子P1和P2执行一个不确定的周期,在该周期中,每秒生成0到100之间的一个随机整数。在每次绘制时,孩子们交流由父P进程生成的数字,该数字将它们相加并打印在屏幕并将它们存储在一个文件中。进程P1必须处理SIGINT中断信号。特别是,在此信号到达时,P1必须显示警告消息“ P1 process busy!”。当程序验证从子进程接收到的数字的总和取值为100时,将由父P进程终止。
现在,我需要一些有关孩子与父母之间同步的帮助。我试图使用信号量,但看起来似乎不可能。我可以使用什么来同步它们?信号?怎么样?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#define READ 0
#define WRITE 1
void handler(int sig){
printf("process 1 is busy\n");
}
void codeprocess1(int pd[], sem_t *sem1){
int i = 0;
int numgenerated;
while( i = 0){
signal(SIGUSR1, handler);
numgenerated = rand()%101;
close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(sem1);
}
}
void codeprocess2(int pd[], sem_t *sem2){
int i = 0;
int numgenerated;
while( i = 0){
numgenerated = rand()%101;
close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(sem2);
}
}
int main(){
pid_t pid1, pid2;
int sum, numread1, numread2, pipe1[2], pipe2[2];
sem_t *sem2 = sem_open("semaph2", O_CREAT | O_EXCL, 1, 0);
sem_t *sem1 = sem_open("semaph1", O_CREAT | O_EXCL, 1, 0);
if(pipe(pipe1)<0){
exit(1);
}
if(pipe(pipe2)<0){
exit(1);
}
pid1 = fork();
switch(pid1){
case -1:
exit(1);
case 0:
codeprocess1(pipe1, sem1);
break;
default:
pid2= fork();
switch( pid2){
case -1:
exit(1);
case 0:
codeprocess2(pipe2, sem2);
break;
default:
while(sum!=1000){
close(pipe1[WRITE]);
read(pipe1[READ], &numread1, sizeof(int));
close(pipe2[WRITE]);
read(pipe2[READ], &numread2, sizeof(int));
sum = sum + numread1 + numread2;
printf("%d\n", sum);
sem_post(sem1);
sem_post(sem2);
}
kill(0, SIGKILL);
}
}
}
答案 0 :(得分:1)
我在这里报告sem_overview(7)
手册页的相关部分:
POSIX semaphores come in two forms: named semaphores and unnamed sema‐
phores.
Named semaphores
A named semaphore is identified by a name of the form /somename;
that is, a null-terminated string of up to NAME_MAX-4 (i.e.,
251) characters consisting of an initial slash, followed by one
or more characters, none of which are slashes. Two processes
can operate on the same named semaphore by passing the same name
to sem_open(3).
The sem_open(3) function creates a new named semaphore or opens
an existing named semaphore. After the semaphore has been
opened, it can be operated on using sem_post(3) and sem_wait(3).
When a process has finished using the semaphore, it can use
sem_close(3) to close the semaphore. When all processes have
finished using the semaphore, it can be removed from the system
using sem_unlink(3).
Unnamed semaphores (memory-based semaphores)
An unnamed semaphore does not have a name. Instead the sema‐
phore is placed in a region of memory that is shared between
multiple threads (a thread-shared semaphore) or processes (a
process-shared semaphore). A thread-shared semaphore is placed
in an area of memory shared between the threads of a process,
for example, a global variable. A process-shared semaphore must
be placed in a shared memory region (e.g., a System V shared
memory segment created using shmget(2), or a POSIX shared memory
object built created using shm_open(3)).
Before being used, an unnamed semaphore must be initialized
using sem_init(3). It can then be operated on using sem_post(3)
and sem_wait(3). When the semaphore is no longer required, and
before the memory in which it is located is deallocated, the
semaphore should be destroyed using sem_destroy(3).
您正在尝试在标准内存中使用未命名信号量。但是它们仅用于同步线程,而不用于同步进程。
我建议使用由共享内存支持的命名信号量(应该更容易)或未命名信号量(通过shmget()
或shm_open()
获取,然后与sem_init()
一起使用-父进程和分叉进程必须使用相同的共享内存段才能访问进程间信号量。
实际上,在您的代码sem1
和sem2
中,它们在主进程中初始化,不会传播到分支的进程:它们具有独立的内存区域和地址,并且不能共享。
编辑后,关于信号量,存在许多问题:
while (i=0)
...哎呀,尝试使用-Wall
进行编译。sem_open()
的返回码ls -l /dev/shm
查看它们,最后只需使用rm
删除它们即可。man 2 open
。/
开头,请参见man sem_overview
这是修改后的代码,内嵌一些注释:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#include <errno.h>
#define READ 0
#define WRITE 1
#define SEM1_NAME "/semaph_1a"
#define SEM2_NAME "/semaph_2a"
void handler(int sig) {
printf("process 1 is busy\n");
}
void codeprocess1(int pd[]) {
int i = 0;
int numgenerated;
// each process must open the handle to the same named semaphore.
// they cannot share a local memory address.
sem_t *my_sem = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
if (my_sem==SEM_FAILED) {
printf("semaphore creation failed, errno=%d\n", errno);
exit(1);
}
// the seed for the two children must be different or they will be generating the same
// sequence of random numbers.
srand(3333);
while(i == 0) {
signal(SIGUSR1, handler);
numgenerated = rand()%101;
// close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(my_sem);
}
}
void codeprocess2(int pd[]){
int i = 0;
int numgenerated;
sem_t *my_sem = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
if (my_sem==SEM_FAILED) {
printf("semaphore creation failed, errno=%d\n", errno);
exit(1);
}
srand(1111);
while(i == 0) {
numgenerated = rand()%101;
// close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(my_sem);
}
}
int main(){
pid_t pid1, pid2;
int sum, numread1, numread2, pipe1[2], pipe2[2];
// O_EXCL removed
// the mode flag must be set to 0777 for example, not "1".
// return value check added
sem_t *sem1 = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
if (sem1==SEM_FAILED) {
printf("semaphore sem1 creation failed, errno=%d\n", errno);
exit(1);
}
sem_t *sem2 = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
if (sem2==SEM_FAILED) {
printf("semaphore sem2 creation failed, errno=%d\n", errno);
exit(1);
}
if (pipe(pipe1) < 0 ) {
exit(1);
}
if (pipe(pipe2) < 0) {
exit(1);
}
pid1 = fork();
switch(pid1){
case -1:
exit(1);
case 0:
codeprocess1(pipe1);
break;
default:
pid2= fork();
switch( pid2) {
case -1:
exit(1);
case 0:
codeprocess2(pipe2);
break;
default:
// 100, not 1000
while (sum != 100) {
// all the "close()" calls are commented out
// close(pipe1[WRITE]);
read(pipe1[READ], &numread1, sizeof(int));
// close(pipe2[WRITE]);
read(pipe2[READ], &numread2, sizeof(int));
// sum must not be incremented
sum = numread1 + numread2;
printf("%d\n", sum);
sem_post(sem1);
sem_post(sem2);
}
kill(0, SIGKILL);
}
}
}
答案 1 :(得分:0)
您的问题确实有很多事情发生。
如答案@Sigismondo中所述,您将多线程与多进程编程混淆了。他们有不同的沟通方式。
为简化起见,线程共享相同的内存,因此一个线程可以查看诸如信号量互斥等全局变量的值:如果一个线程对其进行了修改,则另一个线程将受到影响。
在多处理中,当您fork()
时,将使用其自己的内存空间来生成一个新进程。紧接着fork()
变量值几乎相同(除了pid
,ppid
等),但它们位于不同的存储空间中:如果您有一个仅由一个执行的代码块进程,对其进行修改不会影响其他进程的变量(程序中的信号灯)。
在您的情况下:首先,如果子进程执行相同的操作(即生成一个随机数),为什么您必须使用不同的功能?你不能做类似的事情吗?
#include<stdlib.h>
int generateRand()
{
n = rand() % 100 + 1; //should be random in [1, 100]
}
处理信号
进程P1必须处理SIGINT中断信号。特别是在 该信号P1的到来必须显示警告消息“ P1 进程忙!”。 它验证从其接收到的数字的总和 子进程将假定值为100。
我认为这还不清楚。父级应该捕获SIGINT
信号。孩子们应该怎么办?从您所说的看来,他们似乎不应该抓住这个信号。在这种情况下,您必须查看信号掩码:基本上,您必须在父级中阻塞信号,调用fork()
,然后放回原始掩码。现在您应该更深入一些,但是像这样(here)
sigset_t *parent_mask, *child_mask
//get the current mask
if (int res = sigprocmask (0, NULL, child_mask)<0)
printf("some error\n");
//make the mask block the signal
if (int res = sigaddset(child_mask, SIGINT)<0)
printf("some error in sigaddset \n");
// block the signal with the new mask
if (int res = sigprocmask (SIG_SETMASK, child_mask, parent_mask)<0)
printf("some error\n");
//do your forks: children will inherit the current mask and will not catch SIGINT
...
fork()
...
fork()
....
//set back the original mask so the parent catches SIGINT
if (int res = sigprocmask (SIG_SETMASK, parent_mask, NULL)<0)
printf("some error\n");
This answer of mine,尽管对于多线程来说应该更清楚一些。
信号处理程序
为什么要在codeprocess1(int pd[])
中注册信号处理程序?我一点都不明白。以及为什么SIGUSR1
?
您应该在父级执行此操作(在fork()
之前或之后不应更改,因为该信号已被子级阻止:这取决于您是否希望用户在启动{{是否为1}}:在第一种情况下,请在forks()
之后注册信号处理程序,否则将其放在fork()
的开头。在两种情况下,您都应该这样做:
main()
现在是程序的核心:要进行程序通信,您可以将signal(SIGINT, handler);
与文件描述符一起使用:检查here。
您需要两个文件描述符(每个子进程一个,然后关闭该进程未使用的结尾(读/写))。 考虑一个子进程:
pipe()
退出条件既基于总和(100)的值,也取决于按下CTRL + C的事实。前者在上面的代码中很明显。对于后者,您可以声明一个全局变量(我使用int p = fork();
int fd1[2]; //file descriptor for child1
int fd2[2]; //file descriptor for child2
if (p>0)//parent
{
close(fd1[1]);//close writing end
int n;
read(fd1[0], &n, sizeof(n));
//you might to call the other fork here and redo the same stuff
int p2 = fork();
if (p2>0)
{
close(fd2[1]);//close writing end
int n2;
read(fd2[0], &n2, sizeof(n2));
sum = n2+n1
if (sum==100 && exit = 1)
{
kill(p, SIGKILL);
kill(p2, SIGKILL);
}
}
}
else if(p==0)//child
{
close(fd1[0]);//close read end
int rand_n = generateRand();//or whaterver the name
wrote(fd1[1], &rand_n, sizeof(rand_n));
}
),如果尚未按下0 CTRL + C,则它是一个全局变量。在上面代码的退出条件中检查此值。您的处理程序将负责编写此变量:
exit
请注意,//global variable here
int exit = 0;
void handler(int signo)
{
print("Parent busy doing stuff\n");
exit =1;
}
是由父母写的一件事,因为它仅写在仅由父母调用的处理程序中,并且在仅由父母执行的部分代码中读取:子元素读取它的值,对他们来说总是0。
由于您的问题过于笼统,我尝试给出一些提示:由于我没有尝试过,所以我的代码中可能存在错误。你应该学习自己的。如果您将提供一个最小的工作示例,我将尽力提供帮助。