IPC_NOWAIT semop() 缓冲 SysV 进程

Posted

技术标签:

【中文标题】IPC_NOWAIT semop() 缓冲 SysV 进程【英文标题】:IPC_NOWAIT semop() Buffer SysV Processes 【发布时间】:2013-12-27 14:30:53 【问题描述】:

这段代码我真的很麻烦。 我有一个缓冲区和一个 Produce() 方法,它应该是非阻塞的,这意味着当许多进程尝试 Produce() 时,所有进程都应该返回/或失败,除了一个进程。

我在 man semop() 中读到,当我们使用 IPC_NOWAIT 时,如果信号量已经在使用中,进程应该会失败。但这意味着什么?它返回一些东西?它做了一个 Exit() 吗? 我真的不知道会发生什么。

在我的代码中,有时我在缓冲区中发现 2 条消息,有时发现 1 条。 因为我使用的是 IPC_NOWAIT,所以最后我应该在缓冲区中只有 1 条消息,因为其他进程应该失败,因为它们已经一起启动了!

这里是Produce() 代码:

msg_t* put_non_bloccante(buffer_t* buffer, msg_t* msg)

    ...

    struct sembuf sb;
    sb.sem_flg=IPC_NOWAIT;


    int x=0;


    sb.sem_num=VUOTE; sb.sem_op=-1;
    if ((x=semop(buffer->semid, &sb,1))<0) 
        printf("\n DENTROOOO DOWN%d VUOTE \n",x);/* down(VUOTE) */
        perror("semop() producer down(VUOTE)");
        exit(-9);
    

    sb.sem_num=USO_D; sb.sem_op=-1;
    if ((x=semop(buffer->semid, &sb,1))<0)   /* down(USO_D) */
        printf("\n DENTROOOO DO%dWN USO D \n",x);
        perror("semop() producer down(USO_D)");
        exit(-10);
    


    if((buffer->msg_presenti)< (buffer->size))

        /*HERE DROP THE MESSAGE IN THE BUFFER IF IS NOT FULL*/

    

    sb.sem_num=USO_D; sb.sem_op= 1;
    if (semop(buffer->semid, &sb,1)<0)   /* up(USO_D) */
        printf("\n DENTROOOO UP USO D \n");
        perror("semop() producer up(USO_D)");
        exit(-11);
    


    sb.sem_num=PIENE; sb.sem_op= 1;
    if (semop(buffer->semid, &sb,1)<0) 
        printf("\n DENTROOOO UP PIENE \n");/* up(PIENE) */
        perror("semop() producer up(PIENE)");
        exit(-12);
    

         int delay;
    delay = (int)(random() % 5 ) / 2 + 1;
    sleep(delay);



    

        shmdt(buffer);
        shmdt(sa);
        shmdt(array_msg); */

    return msg;

这是我的简单 CUNIT 测试:

void test_bufferVuoto_3P_NonBlocking_Concurrently(void)

    pid_t pid=-1;
    msg_t* msg = msg_init_string("ciao");
    pid_t cons_pid[3];
    buffer_t* b=buffer_init(3);
    int k;


    for(k=0;k<3 && pid!=0;k++) 
        pid = cons_pid[k]=fork();
    
    switch(pid)
    case -1:
        printf("error fork()");
        exit(-5);
        break;
    case 0:
        buffer_attach(b->bufferid);
        msg_attach(msg->msg_id);
        put_non_bloccante(b,msg);
        msg_deattach(msg);
        buffer_deattach(b);
        sleep(17);
        exit(-5);
    

    sleep(12);
    int j=0;
    for(j=0; j<3; j++) 
        kill(cons_pid[j],SIGKILL); // ... and Kill processes
        wait(NULL);
    

    CU_ASSERT_EQUAL(b->msg_presenti,1);
    CU_ASSERT(0==strcmp("ciao", (b->array_msg[0])->content)  );


    msg_destroy_string(msg);
    buffer_destroy(b);

    return;

我还阅读了 SemOP() 和 IPC_NOWAIT 中的一个 BUG,我不知道是否与此有关: *这是不可取的,因为它可能会迫使 进程终止以阻塞任意长时间。其他 可能性是这样的信号量调整可以被忽略。 gether(有点类似于在指定 IPC_NOWAIT 时失败 对于信号量操作)。 Linux采用第三种方法:递减 信号量值尽可能(即为零)并允许 进程终止以立即进行。 在内核 2.6.x 中,x

【问题讨论】:

【参考方案1】:

据我所知,信号量正在按照您的要求执行,但您对应该发生的事情的概念有点偏差。

如果您使用 IPC_NOWAIT 并且进程无法获取信号量,那么它将立即返回 -1 和 errno == EAGAIN。通常你会利用这段时间做其他事情,然后再试一次。您将立即退出进程,可能同时持有一个或多个信号量。

更糟糕的是,您似乎至少有 3 个信号量在运行。你减少 VUOTE 和 USO_D,做一些事情,然后增加 USO_D 和 PIENE,让 VUOTE 被锁定。这是错字、错误还是我的误解?

总体而言,您对应该发生的事情的期望是错误的。您的代码完全有可能以 1,2 或 3 条消息结尾。 (如果在持有信号量的情况下退出,则可能为 0。)换句话说,取决于操作系统如何调度它们,所有 3 个进程都可以获取信号量并成功,或者可能只有一两个。这是不确定的。

如果真的你的意图是只有一个进程可以成功,那么只需将信号量初始化为 1 并且永远不要增加它。获取它的第一个过程将继续进行,其余的将失败,因为它永远不会再允许其他任何东西通过。

编辑

if ((x = semop(buffer->semid, &sb, 1)) < 0)

    printf("\n DENTROOOO DOWN%d VUOTE \n", x); /* down(VUOTE) */
    perror("semop() producer down(VUOTE)");
    exit( -9);

这里semop 调用 如果无法获得锁,则立即返回将 x 设置为 -1 并将 errno 设置为 EAGAIN。 正在对其进行编码以退出该过程。您可以对条件进行编码以执行其他操作。

if ((x = semop(buffer->semid, &sb, 1)) < 0)

    if (errno == EAGAIN)
        return NULL;  //OR WHATEVER you think appropriate
    else
       //some other failure that's not EAGAIN
        printf("\n DENTROOOO DOWN%d VUOTE \n", x); /* down(VUOTE) */
        perror("semop() producer down(VUOTE)");
        exit( -9);
    


//ELSE you got the lock

除了(间接)通过使用信号量之外,进程彼此不了解。他们每个人都将执行此代码。有些会成功,有些不会。

这不适用于 IPC_NOWAIT 的原因是因为完全有可能每个进程都不必等待锁定。根据它们的安排方式,一个可能会获得锁定并执行,然后是下一个和下一个。或者有些会,有些不会。这是无法预测的。

【讨论】:

感谢您的回答。首先,信号量的设置是好的,VUOTE 是一个 N 信号量(意味着 N 锁)。没有 IPC_NOWAIT 标志一切都可以正常工作。问题是我需要使用它来实现非阻塞版本。您说:“该进程无法获取信号量,然后它将立即返回 -1 和 errno == EAGAIN”但是返回什么?整个过程?它会跳过所有代码吗?或 semop() 返回?什么价值?让我们假设所有的信号量都被锁定了,在这行会发生什么:" if ((x=semop(buffer->semid, &sb,1)) 再次感谢您。但是既然你说 "if ((x = semop(buffer->semid, &sb, 1)) @user244050,因为它没有失败。它得到了锁。这就是我一直试图解释的。您的所有 3 个进程都完全有可能获得锁定并写入消息。 没有任何东西 阻止 proc1 获得锁定和写入。然后 proc2 出现,获取锁并写入。然后是proc3。 无法保证所有 3 个进程都会同时尝试获取锁,这是它们唯一失败的时间 - 当某个其他进程已经拥有锁时。 您说:“进程无法获取信号量,然后它会立即返回 -1 ”他们可能会获得 VUOTE 上的所有锁定,因为它是一个 N 信号量,但只有一个进程应该获得锁定 USO_D,因为它是一个二进制信号量!! 是的,一个进程将一次获取二进制 sem。如果第二个进程试图获取它第一个拥有它,那么第二个进程将因 EAGAIN 而失败。但是,如果第二个 proc 第一个解锁它之后尝试获取它,那么第二个将成功并写入。您不了解所涉及的操作系统切片的调度。仅仅因为您同时启动了 3 个进程,意味着它们都将同时被调度并同时到达锁。

以上是关于IPC_NOWAIT semop() 缓冲 SysV 进程的主要内容,如果未能解决你的问题,请参考以下文章

信号量的基本概念与使用semget,semop

Linux进程间通信 -- 信号量 semget()semop()semctl()

禁用输出缓冲

请教Linux关于UDP最大缓冲区设置

修改socket缓冲区大小

linux udp setsockopt函数啥进修