为什么我的信号量允许分叉进程中的乱序事件?
Why are my semaphores allowing out-of-order events within forked processes?
我正在尝试制作一个程序,从 0 计数到 C 命令行中输入的任何数字。在这个程序中,必须有两个 fork() 调用,总共有 3 个进程。然后我必须至少使用 1 个信号量来确保进程 运行 按数字顺序排列,每个进程负责不同的 n % 3.
我遇到的问题是,尽管我正在使用信号量,但该程序似乎经常 运行 出现故障。我目前正在使用一个门式系统,其中每个进程都会强制其自己指定的信号量等待,一旦完成,sem_post 进程的信号量应该是 运行 接下来。我知道这不是执行此操作的最漂亮或逻辑上最有效的方法,但在我的前两次不同尝试遇到同样的问题后,我非常确定这是一种可行的方法。
如果有人能就我哪里做错了提出任何建议,我将不胜感激。
我的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME1 "/sem1.mutex"
#define SEM_NAME2 "/sem2.mutex"
#define SEM_NAME3 "/sem3.mutex"
int main(int argc, char *argv[]) {
if(argc <= 1){
printf("No arguments were provided so there is no number to count to");
return 1;
}
// Create the 3 semaphores needed
sem_t *sem1;
sem_t *sem2;
sem_t *sem3;
//initialize to 0
sem1 = sem_open(SEM_NAME1, O_CREAT, O_RDWR, 0);
if (sem1==SEM_FAILED) {
printf("%s sem_open failed!", SEM_NAME1);
return (-1);
}
//initialize to 1
sem2 = sem_open(SEM_NAME2, O_CREAT, O_RDWR, 1);
if (sem2==SEM_FAILED) {
printf("%s sem_open failed!", SEM_NAME2);
return (-1);
}
//initialize to 1
sem3 = sem_open(SEM_NAME3, O_CREAT, O_RDWR, 1);
if (sem3==SEM_FAILED) {
printf("%s sem_open failed!", SEM_NAME3);
return (-1);
}
pid_t pid;
pid_t pid2;
pid = fork();
if(pid == 0){
pid2 = fork();
}
// Shared fork variables
int counter = 0;
int ranOnce = 0;
int max_num = atoi(argv[1]);
while(counter <= max_num){
if(pid > 0){
printf("%d",getpid());
if(ranOnce == 0){
counter += 1;
ranOnce = 1;
}
sem_wait(sem2);
printf(" %d \n", counter);
counter += 3;
sem_post(sem3);
}
else if(pid2 == 0){
printf("%d",getpid());
if(ranOnce == 0){
counter += 0;
ranOnce = 1;
}
sem_wait(sem1);
printf(" %d \n", counter);
counter += 3;
sem_post(sem2);
}
else{
printf("%d",getpid());
if(ranOnce == 0){
counter += 2;
ranOnce = 1;
}
sem_wait(sem3);
printf(" %d \n", counter);
counter += 3;
sem_post(sem1);
}
}
//sem_unlink(SEM_NAME1);
//sem_unlink(SEM_NAME2);
//sem_unlink(SEM_NAME3);
return 0;
}
信号量初始值必须取反
sem1 = sem_open(SEM_NAME1, O_CREAT, O_RDWR, 1);
sem2 = sem_open(SEM_NAME2, O_CREAT, O_RDWR, 0);
sem3 = sem_open(SEM_NAME3, O_CREAT, O_RDWR, 0);
您当前同时激活 sem2 和 sem3 进程,因此它们同时工作,但您需要同步它们以删除乱序事件。
抱歉,您的代码无法按设计运行。
在 fork
调用之后,每个进程都有自己的 "shared" 变量的私有副本。与线程不同,您必须使用通过 SysV 共享内存原语设置的共享内存区域。
因此,每个进程都在增加自己的副本,不是counter
的共享副本。
此外,所有对共享变量的访问[包括while
子句]必须封装在锁中。
I'm not sure if you are implying in the third sentence that I want to use a shared variable or that I should be using the individual copies.
我在看你的评论:// Shared fork variables
,所以我推断你想要一个普通柜台。 IMO,你确实想要一个作为交叉检查
I am aware that each is only accessing their own individual copy of the counter variable and that is the purpose of the semaphore gates. When the thread is allowed to run again, it means that both other processes have done their print, so the next character in the sequence will be 3 ahead of the current value of counter.
我重新编写了您的应用程序以简化它,以便我能够理解它。
我没有看到干净的循环序列。如果你是,现在,一切都很好,我可能引入了一个错误。我使用 shmget
等添加了一个共享内存全局计数器。阿尔。它应该增加一个,但它偶尔会向后滑动 [警告: 来晚了,我累了,所以这可能是问题的一部分:-)]
无论如何,这是我想出的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SEM_NAME1 "/sem1.mutex"
#define SEM_NAME2 "/sem2.mutex"
#define SEM_NAME3 "/sem3.mutex"
typedef struct tsk {
int tsk_idx;
sem_t *tsk_sem;
pid_t tsk_pid;
char tsk_name[100];
} tsk_t;
#define NTASK 3
tsk_t tsklist[NTASK];
// Shared fork variables
volatile int counter = 0;
volatile int ranOnce = 0;
int max_num;
volatile int *globptr;
void
dochild(tsk_t *tsk)
{
int stopflg;
int tidx;
tsk_t *tsk2;
while (1) {
sem_wait(tsk->tsk_sem);
stopflg = (counter > max_num);
if (! stopflg) {
printf("Tidx:%d Pid:%d Seq:%d",tsk->tsk_idx,tsk->tsk_pid,*globptr);
*globptr += 1;
if (ranOnce == 0) {
counter += 1;
ranOnce = 1;
}
printf(" Counter:%d\n", counter);
fflush(stdout);
counter += 3;
}
tidx = tsk->tsk_idx + 1;
tidx %= NTASK;
tsk2 = &tsklist[tidx];
sem_post(tsk2->tsk_sem);
if (stopflg)
break;
}
exit(0);
}
int
main(int argc, char *argv[])
{
tsk_t *tsk;
int tidx;
void *sptr;
if (argc <= 1) {
printf("No arguments were provided so there is no number to count to\n");
//return 1;
}
else
max_num = atoi(argv[1]);
int shmfd = shmget(IPC_PRIVATE,sizeof(int),0600);
sptr = shmat(shmfd,NULL,0);
globptr = sptr;
*globptr = 0;
// Create the 3 semaphores needed
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
tsk->tsk_idx = tidx;
sprintf(tsk->tsk_name,"/sem%d.mutex",tidx);
if (max_num == 0) {
sem_unlink(tsk->tsk_name);
continue;
}
tsk->tsk_sem = sem_open(tsk->tsk_name, O_CREAT, O_RDWR,
0644,(tidx == 0) ? 1 : 0);
if (tsk->tsk_sem == SEM_FAILED) {
printf("%s sem_open failed! -- %s\n",tsk->tsk_name,strerror(errno));
return (-1);
}
}
if (max_num == 0)
return 0;
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
tsk->tsk_pid = fork();
if (tsk->tsk_pid != 0)
continue;
tsk->tsk_pid = getpid();
dochild(tsk);
}
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
waitpid(tsk->tsk_pid,NULL,0);
}
#if 1
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
sem_unlink(tsk->tsk_name);
}
#endif
shmdt(sptr);
return 0;
}
这是 运行 的输出:
Tidx:0 Pid:18563 Seq:0 Counter:1
Tidx:0 Pid:18563 Seq:1 Counter:4
Tidx:0 Pid:18563 Seq:2 Counter:7
Tidx:0 Pid:18563 Seq:3 Counter:10
Tidx:1 Pid:18564 Seq:4 Counter:1
Tidx:1 Pid:18564 Seq:5 Counter:4
Tidx:1 Pid:18564 Seq:6 Counter:7
Tidx:1 Pid:18564 Seq:7 Counter:10
Tidx:2 Pid:18565 Seq:4 Counter:1
Tidx:2 Pid:18565 Seq:9 Counter:4
Tidx:2 Pid:18565 Seq:10 Counter:7
Tidx:2 Pid:18565 Seq:11 Counter:10
我正在尝试制作一个程序,从 0 计数到 C 命令行中输入的任何数字。在这个程序中,必须有两个 fork() 调用,总共有 3 个进程。然后我必须至少使用 1 个信号量来确保进程 运行 按数字顺序排列,每个进程负责不同的 n % 3.
我遇到的问题是,尽管我正在使用信号量,但该程序似乎经常 运行 出现故障。我目前正在使用一个门式系统,其中每个进程都会强制其自己指定的信号量等待,一旦完成,sem_post 进程的信号量应该是 运行 接下来。我知道这不是执行此操作的最漂亮或逻辑上最有效的方法,但在我的前两次不同尝试遇到同样的问题后,我非常确定这是一种可行的方法。
如果有人能就我哪里做错了提出任何建议,我将不胜感激。
我的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME1 "/sem1.mutex"
#define SEM_NAME2 "/sem2.mutex"
#define SEM_NAME3 "/sem3.mutex"
int main(int argc, char *argv[]) {
if(argc <= 1){
printf("No arguments were provided so there is no number to count to");
return 1;
}
// Create the 3 semaphores needed
sem_t *sem1;
sem_t *sem2;
sem_t *sem3;
//initialize to 0
sem1 = sem_open(SEM_NAME1, O_CREAT, O_RDWR, 0);
if (sem1==SEM_FAILED) {
printf("%s sem_open failed!", SEM_NAME1);
return (-1);
}
//initialize to 1
sem2 = sem_open(SEM_NAME2, O_CREAT, O_RDWR, 1);
if (sem2==SEM_FAILED) {
printf("%s sem_open failed!", SEM_NAME2);
return (-1);
}
//initialize to 1
sem3 = sem_open(SEM_NAME3, O_CREAT, O_RDWR, 1);
if (sem3==SEM_FAILED) {
printf("%s sem_open failed!", SEM_NAME3);
return (-1);
}
pid_t pid;
pid_t pid2;
pid = fork();
if(pid == 0){
pid2 = fork();
}
// Shared fork variables
int counter = 0;
int ranOnce = 0;
int max_num = atoi(argv[1]);
while(counter <= max_num){
if(pid > 0){
printf("%d",getpid());
if(ranOnce == 0){
counter += 1;
ranOnce = 1;
}
sem_wait(sem2);
printf(" %d \n", counter);
counter += 3;
sem_post(sem3);
}
else if(pid2 == 0){
printf("%d",getpid());
if(ranOnce == 0){
counter += 0;
ranOnce = 1;
}
sem_wait(sem1);
printf(" %d \n", counter);
counter += 3;
sem_post(sem2);
}
else{
printf("%d",getpid());
if(ranOnce == 0){
counter += 2;
ranOnce = 1;
}
sem_wait(sem3);
printf(" %d \n", counter);
counter += 3;
sem_post(sem1);
}
}
//sem_unlink(SEM_NAME1);
//sem_unlink(SEM_NAME2);
//sem_unlink(SEM_NAME3);
return 0;
}
信号量初始值必须取反
sem1 = sem_open(SEM_NAME1, O_CREAT, O_RDWR, 1);
sem2 = sem_open(SEM_NAME2, O_CREAT, O_RDWR, 0);
sem3 = sem_open(SEM_NAME3, O_CREAT, O_RDWR, 0);
您当前同时激活 sem2 和 sem3 进程,因此它们同时工作,但您需要同步它们以删除乱序事件。
抱歉,您的代码无法按设计运行。
在 fork
调用之后,每个进程都有自己的 "shared" 变量的私有副本。与线程不同,您必须使用通过 SysV 共享内存原语设置的共享内存区域。
因此,每个进程都在增加自己的副本,不是counter
的共享副本。
此外,所有对共享变量的访问[包括while
子句]必须封装在锁中。
I'm not sure if you are implying in the third sentence that I want to use a shared variable or that I should be using the individual copies.
我在看你的评论:// Shared fork variables
,所以我推断你想要一个普通柜台。 IMO,你确实想要一个作为交叉检查
I am aware that each is only accessing their own individual copy of the counter variable and that is the purpose of the semaphore gates. When the thread is allowed to run again, it means that both other processes have done their print, so the next character in the sequence will be 3 ahead of the current value of counter.
我重新编写了您的应用程序以简化它,以便我能够理解它。
我没有看到干净的循环序列。如果你是,现在,一切都很好,我可能引入了一个错误。我使用 shmget
等添加了一个共享内存全局计数器。阿尔。它应该增加一个,但它偶尔会向后滑动 [警告: 来晚了,我累了,所以这可能是问题的一部分:-)]
无论如何,这是我想出的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SEM_NAME1 "/sem1.mutex"
#define SEM_NAME2 "/sem2.mutex"
#define SEM_NAME3 "/sem3.mutex"
typedef struct tsk {
int tsk_idx;
sem_t *tsk_sem;
pid_t tsk_pid;
char tsk_name[100];
} tsk_t;
#define NTASK 3
tsk_t tsklist[NTASK];
// Shared fork variables
volatile int counter = 0;
volatile int ranOnce = 0;
int max_num;
volatile int *globptr;
void
dochild(tsk_t *tsk)
{
int stopflg;
int tidx;
tsk_t *tsk2;
while (1) {
sem_wait(tsk->tsk_sem);
stopflg = (counter > max_num);
if (! stopflg) {
printf("Tidx:%d Pid:%d Seq:%d",tsk->tsk_idx,tsk->tsk_pid,*globptr);
*globptr += 1;
if (ranOnce == 0) {
counter += 1;
ranOnce = 1;
}
printf(" Counter:%d\n", counter);
fflush(stdout);
counter += 3;
}
tidx = tsk->tsk_idx + 1;
tidx %= NTASK;
tsk2 = &tsklist[tidx];
sem_post(tsk2->tsk_sem);
if (stopflg)
break;
}
exit(0);
}
int
main(int argc, char *argv[])
{
tsk_t *tsk;
int tidx;
void *sptr;
if (argc <= 1) {
printf("No arguments were provided so there is no number to count to\n");
//return 1;
}
else
max_num = atoi(argv[1]);
int shmfd = shmget(IPC_PRIVATE,sizeof(int),0600);
sptr = shmat(shmfd,NULL,0);
globptr = sptr;
*globptr = 0;
// Create the 3 semaphores needed
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
tsk->tsk_idx = tidx;
sprintf(tsk->tsk_name,"/sem%d.mutex",tidx);
if (max_num == 0) {
sem_unlink(tsk->tsk_name);
continue;
}
tsk->tsk_sem = sem_open(tsk->tsk_name, O_CREAT, O_RDWR,
0644,(tidx == 0) ? 1 : 0);
if (tsk->tsk_sem == SEM_FAILED) {
printf("%s sem_open failed! -- %s\n",tsk->tsk_name,strerror(errno));
return (-1);
}
}
if (max_num == 0)
return 0;
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
tsk->tsk_pid = fork();
if (tsk->tsk_pid != 0)
continue;
tsk->tsk_pid = getpid();
dochild(tsk);
}
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
waitpid(tsk->tsk_pid,NULL,0);
}
#if 1
for (tidx = 0; tidx < NTASK; ++tidx) {
tsk = &tsklist[tidx];
sem_unlink(tsk->tsk_name);
}
#endif
shmdt(sptr);
return 0;
}
这是 运行 的输出:
Tidx:0 Pid:18563 Seq:0 Counter:1
Tidx:0 Pid:18563 Seq:1 Counter:4
Tidx:0 Pid:18563 Seq:2 Counter:7
Tidx:0 Pid:18563 Seq:3 Counter:10
Tidx:1 Pid:18564 Seq:4 Counter:1
Tidx:1 Pid:18564 Seq:5 Counter:4
Tidx:1 Pid:18564 Seq:6 Counter:7
Tidx:1 Pid:18564 Seq:7 Counter:10
Tidx:2 Pid:18565 Seq:4 Counter:1
Tidx:2 Pid:18565 Seq:9 Counter:4
Tidx:2 Pid:18565 Seq:10 Counter:7
Tidx:2 Pid:18565 Seq:11 Counter:10