为什么我的信号量允许分叉进程中的无序事件?

时间:2017-04-03 05:31:40

标签: c semaphore

我正在尝试创建一个从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;
    }

2 个答案:

答案 0 :(得分:2)

信号量初始值必须是反向

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进程,因此它们可以同时工作,但您需要同步它们以删除无序事件。

答案 1 :(得分:0)

抱歉,您的代码无法正常使用。

fork来电之后,每个进程都有自己的私有副本&#34;共享&#34;变量。与线程不同,您必须使用通过SysV共享内存原语设置的共享内存区域。

因此,每个进程都在递增自己的副本,而不是 counter的共享副本。

此外,对共享变量的所有访问[包括while子句]必须封装在锁中。

  

我不确定你是否在第三句中暗示我想使用共享变量,或者我应该使用单独的副本。

我在看你的评论:// Shared fork variables,所以我推断你想要一个普通的计数器。 IMO,你想要一个作为交叉检查

  

我知道每个人只访问他们自己的计数器变量副本,这就是信号量门的目的。当允许线程再次运行时,这意味着其他进程都已完成打印,因此序列中的下一个字符将比计数器的当前值提前3个。

我已经重新编写了您的应用以简化它,以便我能理解它。

我没有看到干净的循环序列。如果你现在,一切都很好,我可能已经介绍了一个bug。我使用shmget et添加了一个共享内存全局计数器。人。它应该增加一个,但它偶尔会向后滑动[警告:它已经很晚了,我累了,所以这可能是问题的一部分: - )] < / p>

无论如何,这是我想出的:

#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