【问题标题】:Unnamed semaphore POSIX IPC未命名信号量 POSIX IPC
【发布时间】:2015-04-29 16:57:10
【问题描述】:

我分配了一个整数大小的共享内存段。

stdout 的预期结果应该是:

P: 1
C: 2

但实际上是:

C: 1
P: 2

为什么在父进程完成并解锁共享内存段之前子进程不会被阻塞?

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>
#include <semaphore.h>

#define SHMSEGSIZE sizeof(int)

int main(void){

   pid_t pid;
   int shmID;
   int *shared_mem;

   /* initializing shared memory */
   shmID  = shmget(IPC_PRIVATE, SHMSEGSIZE, IPC_CREAT | 0644);
   shared_mem  = (int *)shmat(shmID, 0, 0);
   *shared_mem = 0;

   /* initializing semaphore */
   sem_t sem;
   int pshared = 1; // !=0 for processes, =0 for threads
   int value = 1; // number of processes at a time
   sem_init(&sem, pshared, value); // initialize the semaphore

   pid = fork();

   if(pid>(pid_t)0){ // parent
       sem_wait(&sem);
       sleep(6);
       *shared_mem += 1;
       printf("P: %d\n", *shared_mem);
       sem_post(&sem);
       exit(EXIT_SUCCESS);
   } // parent

   if(pid==(pid_t)0){ // child
       sleep(3);
       sem_wait(&sem);
       *shared_mem += 1;
       sem_post(&sem);
       printf("C: %d\n", *shared_mem);
       exit(EXIT_SUCCESS);
   } // child

   /* fork() failed */
   printf("Failed to fork().");
   exit(EXIT_FAILURE);
}

编译:

gcc -o executable sem.c -pthread

【问题讨论】:

    标签: c unix posix semaphore


    【解决方案1】:

    如果要“pshared”,sem_t 本身必须在共享内存中。

    struct my_shared_mem {
      sem_t sem;
      int   value;
    };
    
    ... later ...
    
     struct my_shared_mem *shared;
    
     shmID  = shmget(IPC_PRIVATE, sizeof(*shared), ...);
     shared = shmat(shmID, ...);
     shared->value = 0;
     sem_init(&shared->sem, 1, 1);
     ...
    

    【讨论】:

    • 你能说得更具体些吗?如何将其放入共享内存?以某种方式映射它或使用 *shared_mem=sem 将其真正保存在那里?
    • @JohannesKlaus sem_init 的参数必须指向共享内存。我已经更新了答案。 (请注意,您不能只将 sem 复制到共享内存,因为对副本的操作是 undefined。)
    • 不要使用分配的堆栈sem_t,而是使用指向共享内存块某处的指针。
    • @doron 它指向哪里重要吗?还是我必须使我的共享内存段更大(现在是 int 的大小)并保存一个“插槽”供指针指向?
    • 实际的信号量必须驻留在共享内存中,这意味着您需要某种指向它的指针,就像堆上的内存一样。上面的例子是一个完美的做什么你想做的事。另一种方法是使用名称信号量。
    【解决方案2】:

    每当你 fork 时,子进程都会继承父进程地址空间的副本。因此变量 sem 将被复制到孩子的地址空间。在父项中所做的任何更改都不会反映在子项中。 (sem_wait 基本上是递减信号量的值)

    前面提到的一种方法是将信号量放在共享内存段中。
    另一种方法是使用semget 调用创建系统范围的信号量(请参阅man semget)。这里的接口比 pthread 库复杂一点。您必须使用semctl 设置值(使用union semun)并使用semop(使用struct sembuf)执行信号量操作。这个信号量是一个 IPC,它有一个密钥(在 semget 调用中传递)。您可以使用ipcs -s查看系统范围的信号量

    【讨论】:

    • semget 尝试是命名信号量还是未命名信号量?
    • @JohannesKlaus 您不使用名称来识别它,而是使用密钥。从技术上讲,根据here 的定义,它是一个命名信号量。抱歉编辑。我对格式不熟悉:/
    猜你喜欢
    • 1970-01-01
    • 2012-08-01
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    • 2019-11-26
    相关资源
    最近更新 更多