如何在 c 中使用 posix 命名信号量和 Linux 上两个进程之间的共享内存?

Posted

技术标签:

【中文标题】如何在 c 中使用 posix 命名信号量和 Linux 上两个进程之间的共享内存?【英文标题】:how to use posix named semaphore in c with shared memory between two processes on linux? 【发布时间】:2015-10-20 13:56:59 【问题描述】:

我正在尝试同步两个进程以读取和写入共享内存。这里是a_process.c

 #include <fcntl.h>
 #include <sys/mman.h>
 #include <semaphore.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdio.h>

 #define SHM_PATH        "/instant_messaging"
 #define SHM_SIZE         50
 #define SEM_PATH        "/sem_instant_messaging"

 char *sendbuff;

 int main()
 
     //sahred memory setting 
     //shm_unlink(SHM_PATH);
     int shmfd;

     shmfd = shm_open(SHM_PATH, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG);
     if (shmfd < 0) 
         perror("In shm_open()");
         exit(1);
     

     int shm_size = SHM_SIZE;

     ftruncate(shmfd, shm_size); 
     sendbuff = (char*) mmap(NULL, shm_size, PROT_READ | PROT_WRITE, 
             MAP_SHARED, shmfd, 0);
     if (sendbuff == NULL) 
        perror("In mmap()");
        exit(1);
     

     //semaphore setting 
     sem_unlink(SEM_PATH);

     sem_t * sem1;

     sem1 = sem_open(SEM_PATH, O_CREAT, S_IRUSR | S_IWUSR, 1);
     printf("before while\n");
     //setmemshr();
     //setsem();
     while (1) 
         int status = sem_wait(sem1);

         if (status != 0)
         
             perror("in sem_wait");
         
         printf("after wait\n");
         printf("%s\n",sendbuff);
         scanf("%s",sendbuff);
         sem_post(sem1);
         //sleep(3);
     
 

这里是b_process.c:

#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

#define SHM_PATH        "/instant_messaging"
#define SHM_SIZE         50
#define SEM_PATH        "/sem_instant_messaging"  //dddddddddd

char *sendbuff;

int main()

    //sahred memory setting
    //shm_unlink(SHM_PATH); 
    int shmfd;

    shmfd = shm_open(SHM_PATH, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG);
    if (shmfd < 0) 
        perror("In shm_open()");
        exit(1);
    

    int shm_size = SHM_SIZE;

    ftruncate(shmfd,shm_size);
    sendbuff = (char*) mmap(NULL, shm_size, PROT_READ | PROT_WRITE, 
            MAP_SHARED, shmfd, 0);
    if (sendbuff == NULL) 
        perror("In mmap()");
        exit(1);
    

    //semaphore setting 
    sem_unlink(SEM_PATH);

    sem_t *sem1;

    sem1 = sem_open(SEM_PATH, O_CREAT, S_IRUSR | S_IWUSR, 1);   

    while (1) 
        printf("before wait %d\n", *sem1);
        int status = sem_wait(sem1);

        if (status != 0)
        
            perror("in sem_wait");
        
        printf("after wait %d\n", *sem1);
        printf("%s", sendbuff);
        scanf("%s", sendbuff);
        sem_post(sem1);
    

我花了很多时间调试它并阅读手册页,但似乎虽然共享内存有效,信号量发布和等待在每个进程的地址空间内有效,但这两个进程都不会影响对方看到的数据在共享内存区域中。

有人看到我错过了什么吗?

【问题讨论】:

我觉得还可以,为什么说不影响其他进程的值? 是的......像这样的东西最好在它失败的系统/环境上进行测试和调试。 OP,那将是你的。 @Mr.E 当我点击 sem_wait(sem1);它应该阻止另一个进程,但它不会,尽管 *sem1 的值从 1 减少到 0 你不能在代码中找到任何不寻常的东西@MartinJames 我认为从两个程序中取消信号量的链接是个坏主意,因为 sem_unlink 会立即删除信号量名称,但信号量本身会保持活动状态,直到最后一个用户关闭它。这让我觉得您实际上是在处理两个不同的信号量...您可以在您首先启动的程序中仅使用取消链接进行测试吗? 【参考方案1】:

您的代码中有几处可疑之处,但其中一种组合显然是错误的:两个进程在信号量路径上执行sem_unlink(),紧跟其后的是sem_open()。仅当您非常幸运时,这才会产生使用相同信号量的两个 proc 的效果,这样两个 sem_unlink()s 都在执行 sem_open() 之前执行。否则,无论哪个进程进入第二个进程,都会取消第一个进程创建的信号量的链接,而是创建并使用它自己的信号量。这两个进程都将继续进行,每个进程都使用自己独立的信号量。

我还发现这两个进程ftruncate() 共享内存区域有点可疑,尤其是它们在同步之前这样做(或者如果 procs 完全有效同步的话)。我认为您应该让一个进程负责设置共享内存段;另一个只需要打开并映射它。更正您的信号量初始化,并使用信号量确保在任一程序继续使用共享内存之前调整其大小。

【讨论】:

是的,你是对的,我删除了“sem_unlink”并使用“fstat”将共享内存的大小传递给另一个进程(而不是 ftruncate()),它现在正在工作,但它有一些随机行为有时我不知道为什么 很难猜测“随机行为”可能是什么,但可能与b_process.c中的这个错误代码有关:printf("before wait %d\n", *sem1)。没有理由认为 sem_t 对象可以成功地解释为 int,该代码(以及稍后的类似代码)假设了这一点。如果要打印信号量的值,可以通过sem_getvalue() 检索。 我使用了sem_getvalue,虽然这似乎不是问题,但当我在sem_post() 之后简单地放一个sleep(1) 语句时,问题就消失了,似乎它给了其他进程时间在第一个进程再次循环和sem_wait() 第二次循环之前对信号量作出反应。这是约翰先生吗? @mohammed,你还没有解释剩下的问题是什么。如果您希望程序交替但它们没有,那么在每个 sem_post() 之后放置一个 sleep(1) 是一种粗略的技巧,可能会使程序通常按预期合作。对此类问题的更可靠的解决方案是使用两个信号量而不是一个信号量。每个程序都会等待自己的信号量并发布其他程序的信号量。 是的@john 问题是我希望他们交替使用,但他们不会一直这样做而且我当然不打算使用sleep() 和我会尝试你所说的,但是为什么,信号量的行为方式是自然的吗?我看不出这段愚蠢的代码不能正常工作的任何原因是它与 kenel 中的调度有关

以上是关于如何在 c 中使用 posix 命名信号量和 Linux 上两个进程之间的共享内存?的主要内容,如果未能解决你的问题,请参考以下文章

使用 posix 未命名信号量的 IPC [关闭]

如何实现两个 C 程序使用的全局 POSIX 信号量?

POSIX 信号量

命名 POSIX 信号量的问题

是否可以仅使用 POSIX 信号量来避免唤醒等待竞赛?是良性的吗?

用于IPC的命名信号的POSIX实现