未命名信号量 POSIX IPC
Unnamed semaphore POSIX IPC
我分配了一个整数大小的共享内存段。
标准输出的预期结果应该是:
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
sem_t
本身必须在共享内存中才能成为 "pshared"。
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);
...
无论何时您进行分叉,child 进程都会继承 parent 进程地址 space 的副本。因此变量 sem 将被复制到 child 的地址 space。在 parent 中所做的任何更改都不会反映在 child 中。 (sem_wait
基本上是递减信号量的值)
前面提到的一种方法是将信号量放在共享内存段中。
另一种方法是使用 semget
调用创建一个 system-wide 信号量(参考 man semget
)。这里的接口比 pthread 库的接口复杂一点。您将必须使用 semctl
设置值(使用 union semun
)并使用 semop
(使用 struct sembuf
)执行信号量操作。这个信号量是一个具有密钥的 IPC(在 semget
调用中传递)。您可以使用 ipcs -s
查看 system-wide 信号量
我分配了一个整数大小的共享内存段。
标准输出的预期结果应该是:
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
sem_t
本身必须在共享内存中才能成为 "pshared"。
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);
...
无论何时您进行分叉,child 进程都会继承 parent 进程地址 space 的副本。因此变量 sem 将被复制到 child 的地址 space。在 parent 中所做的任何更改都不会反映在 child 中。 (sem_wait
基本上是递减信号量的值)
前面提到的一种方法是将信号量放在共享内存段中。
另一种方法是使用 semget
调用创建一个 system-wide 信号量(参考 man semget
)。这里的接口比 pthread 库的接口复杂一点。您将必须使用 semctl
设置值(使用 union semun
)并使用 semop
(使用 struct sembuf
)执行信号量操作。这个信号量是一个具有密钥的 IPC(在 semget
调用中传递)。您可以使用 ipcs -s