[英]C - Shared memory and semaphores
我想创建一个具有共享内存和信号量的C程序。 应该有两个子进程。 两个孩子都有不同的int数。 然后有一个目标号应写入共享内存中。 现在,两个孩子都应该从目标编号中减去其编号,直到目标编号小于或等于0。我不希望出现比赛条件。 这就是为什么我尝试使用信号量的原因。 但这对我不起作用。 这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/sem.h>
#define SEG_SIZE sizeof(int)
#define NUM_OF_CHILDS 2
int main(int argc, char *argv[]){
int i, shm_id, sem_id, *shar_mem;
int pid[NUM_OF_CHILDS];
long waittime = 100;
unsigned short marker[1];
/* Define the numbers and the goal number */
int numbers[2] = {28, 23};
int goal = (numbers[0] + numbers[1]) * 4;
/* Create semaphor */
if((sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT|0644)) == -1){
perror("semget()");
exit(EXIT_FAILURE);
}
marker[0] = 1;
/* All sem's to 1 */
semctl(sem_id, 1, SETALL, marker);
/* Create shared memory */
if((shm_id = shmget(IPC_PRIVATE, SEG_SIZE, IPC_CREAT|0600)) == -1){
perror("shmget()");
exit(EXIT_FAILURE);
}
if((shar_mem = (int *)shmat(shm_id, 0, 0)) == (int *) -1){
perror("shmat()");
exit(EXIT_FAILURE);
}
*shar_mem = goal;
/* Create child processes */
for(i = 0; i < NUM_OF_CHILDS; i++){
pid[i] = fork();
if(pid[i] < 0){
printf("Error!\n");
exit(1);
}
if(pid[i] == 0){
int count = 0;
/* Child processes */
/* Structs for semaphor */
struct sembuf enter, leave;
enter.sem_num = leave.sem_num = 0;
enter.sem_flg = leave.sem_flg = SEM_UNDO;
enter.sem_op = -1; /* DOWN-Operation */
leave.sem_op = 1; /* UP-Operation */
/* Join critical area */
semop(sem_id, &enter, 1);
while(*shar_mem > 0){
usleep(waittime);
*shar_mem -= numbers[i];
count++;
}
printf("%i\n", count);
/* Leave critical area */
semop(sem_id, &leave, 1);
exit(0);
}
}
/* Wait for childs. */
for(i = 0; i < NUM_OF_CHILDS; i++){
waitpid(pid[i], NULL, 0);
}
/* Is goal equal 0 or lower? */
int returnv;
if(*shar_mem == 0){
/* No race conditions */
returnv = 0;
}
else {
/* Race conditions */
returnv = 1;
}
/* Close shared memory and semaphores */
shmdt(shar_mem);
shmctl(shm_id, IPC_RMID, 0);
semctl(sem_id, 0, IPC_RMID);
return returnv;
}
最后,如果共享内存值为0,则不应存在争用条件。 如果它小于0,则说明存在竞争条件。 而且我总是低于0。最重要的是,我计算每个孩子减去他的数字的次数。 结果:第一个孩子8次,第二个孩子0次。 有人可以帮我吗?
使您的while循环适应这样的情况,以便在减去一次后离开关键部分:
for ( ; ; ) {
usleep(waittime);
semop(sem_id, &enter, 1);
if (*shar_mem <= 0) {
semop(sem_id, &leave, 1);
break;
}
*shar_mem -= numbers[i];
semop(sem_id, &leave, 1);
count++;
}
但是,正如我的评论中所述,不能保证两个孩子都交替减去他们的数字,即结果可能小于零
在您评论的评论中:
我试图将semop部分放入循环中。 现在第一个孩子有8个计数,第二个孩子有1个计数。 [...]我必须使用信号量。
显然,仅将关键部分边界移入循环内部是不够的。 这是因为无法保证当一个进程通过增加信号量而离开关键部分时,另一进程将立即进入关键部分。 相反,第一个过程可能会循环,然后在第二个部分进入关键部分之前重新输入它。 实际上,该结果很可能是因为循环很紧,而且我们知道退出关键部分的进程当前已在某些CPU上调度。
可以通过在关键部分之外的环路中引入延迟来解决该问题,但这有点麻烦。 一方面,它不必要地减慢了程序速度。 另一方面,尽管足够长的延迟可以使问题极不可能出现,但是没有多少延迟可以保证问题不会显现。 您始终受制于系统调度程序。 但是,由于您已经有了延迟,因此您也可以通过将其置于关键部分之外,使所需的行为很可能得到体现。
有几种处理此问题的方法,可以提供各种所需的语义而不会造成人为的延迟。 它们全部涉及至少一个以上的信号量。 以下是两种可能性:
为每个进程创建一个单独的信号灯。 选择一个初始化为1的变量,将其余初始化为0的变量。在循环的顶部,每个进程都会减少自己的信号量,在底部,它会增加下一个进程的信号量。 然后,这些过程以循环方式运行。
除了用于保护关键部分的信号灯之外,还可以使用两个其他信号灯在循环的顶部创建“循环屏障”。 循环屏障是一种可重用的构造,它使一些固定数量的进程/线程在它们中的任何一个可以继续进行之前都到达给定的点。 这不会使进程严格交替,但可以防止任何进程比其他进程提前一个以上的循环迭代。 它的优点是,无论要协调多少个进程,它都只需要三个信号灯。
前者更容易概念化和编写,即使它为流程订购提供了更有力的保证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.