网上的大部分教程讲的都是线程同步,却很少有关于进程同步的博客,但其实线程同步与进程同步还是有些许差别的,故写此博客加以说明.
知识点
1.linux semaphore
头文件#include <semaphore.h>
编译注意事项:注意关联pthread, 即在编译命令之后加上-lthread
主要使用了linux的信号量及相应函数.
- int sem_init(sem_t *sem, int pshared, unsigned int value);//初始化信号量
主要注意第二个与第三个参数,pshared指明信号量是否进程共享,如果pshared为0,则只能在同一个进程中使用,如果不等于0,则能在进程之间共享,但需要使用共享内存的方式共享信号量(详情见下图官方文档说明);value指明能够同时运行的线程或者进程数量,wait会使其减1, post会使其加1
- int sem_wait(sem_t *sem);//如果没有信号,则进程或者线程阻塞,相当于p操作
- int sem_post(sem_t *sem);//释放信号量,相当v操作
- int sem_destroy(sem_t *sem);//销毁信号量
2.共享内存
在实现线程同步时并不需要共享内存,因为只要信号量是一个全局变量,线程之间就能够共享该信号量,但进程与线程有所不同.在创建父子进程时,子进程会将父进程中的变量拷贝一份,所以父子进程在使用同一变量时互不影响,因为子进程只是使用变量的副本.为了实现进程同步,只能使用共享内存的方式共享信号量.
实现
实现进程同步,则需要保证一个进程必须在另一个进程之前运行,如果只使用一个信号量,无法保证两个进程的执行顺序,所以考虑使用两个信号量.大致思路如下(保证进程A先运行):
sem_t sem1, sem2;
/*共享内存*/
/*IPC_CREAT表示在key标识的共享内存不存在时,创建共享内存*/
int shmid1 = shmget((key_t)111, sizeof(sem_t), 0666 | IPC_CREAT);
/*调用成功时返回一个指向共享内存第一个字节的指针*/
void *shm1 = shmat(shmid1, 0, 0);
sem1 = (sem_t *)shm1;
int shmid2 = shmget((key_t)111, sizeof(sem_t), 0666 | IPC_CREAT);
void *shm2 = shmat(shmid2, 0, 0);
sem2 = (sem_t *)shm2;
sem_init(&(sem1), 1, 1);
sem_init(&(sem2), 1, 0);
....
Process A:
sem_wait(&(sem1)); //等待
....
sem_post(&sem2);//释放
Process B:
sem_wait(&(sem2)); //等待
....
sem_post(&sem1);//释放
代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#define N 100
struct shareData
{
sem_t sem1;
sem_t sem2; //2个信号量实现同步操作,需要link pthread
int arr[N];
};
int main()
{
int pid;
/*IPC_CREAT表示在key标识的共享内存不存在时,创建共享内存*/
int shmid = shmget((key_t)1234, sizeof(struct shareData), 0666 | IPC_CREAT);
/*调用成功时返回一个指向共享内存第一个字节的指针*/
void *shm = shmat(shmid, 0, 0);
struct shareData *shareData = (struct shareData *)shm;
/*初始化信号量,第一个1表示信号量可以在进程之间共享,第二个1表示只能同时执行一个进程*/
sem_init(&(shareData->sem1), 1, 1);
sem_init(&(shareData->sem2), 1, 0);
//创建进程
pid = fork();
if (pid > 0)
{
sem_wait(&(shareData->sem1)); //等待
for (int i = 0; i < N; i++)
{
shareData->arr[i] = i + 1; //父进程写入数据
printf("Parent Input:%d\n", shareData->arr[i]);
}
sem_post(&(shareData->sem2)); //释放
}
else if (pid == 0)
{
sem_wait(&(shareData->sem2));
for (int i = 0; i < N; i++)
{
printf("Output:%d\n", shareData->arr[i]); //子进程读出数据
}
sem_post(&(shareData->sem1));
}
else if (pid < 0)
{
return -1;
}
sem_destroy(&(shareData->sem1));
sem_destroy(&(shareData->sem2)); //删除信号量
shmctl(shmid, IPC_RMID, 0); //删除共享内存
return 0;
}