如果说 SystemV 共享内存提供了进程间通信的渠道,SystemV 信号量则是提供了进程间通信的控制方法。通过申请信号量,来获取临界资源的使用权;通过释放信号量来归还临界资源的使用权。
下面将使用两个信号量来控制进程间通信,一个是写信号量,代表可写位置的个数;一个是读信号量,代表可读内容的个数。
目录
1、创建并挂接共享内存
2、创建并初始化信号量集合
3、创建父子进程
(1) PV操作
(2) 父子进程通过共享内存实现进程间通信
4、完整代码
key_t key = ftok(".", 100);
if (key < 0)
{
perror("ftok");
return 1;
}
// 新建共享内存
int shmid = shmget(key, 4*1024, IPC_CREAT | IPC_EXCL | 0666);
if (shmid < 0)
{
perror("shmget");
return 1;
}
// 把当前进场挂接到共享内存上
char* shmaddr = (char*)shmat(shmid, NULL, 0);
if(shmaddr == (void*)-1)
{
perror("shmat");
return 1;
}
// 全局变量
enum SEM
{
SEM_WR,
SEM_RD
};
// 创建信号量集合
int semid = semget(key, 2, IPC_CREAT | 0666);
if (semid < 0)
{
perror("semget");
return 1;
}
// 初始化信号量集合
union semun mysem;
mysem.val = 1;
semctl(semid, SEM_WR, SETVAL, mysem);
mysem.val = 0;
semctl(semid, SEM_RD, SETVAL, mysem);
父进程负责写,子进程负责读,在此之前,我们可以先把PV操作封装成两个函数,方便后续调用。
// P操作
void Poperation(int semid, int semnum)
{
struct sembuf buf;
buf.sem_num = semnum;
buf.sem_op = -1;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
// V操作
void Voperation(int semid, int semnum)
{
struct sembuf buf;
buf.sem_num = semnum;
buf.sem_op = 1;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
// 创建子进程,让父子进程通过共享内存通信,通过信号量控制互斥
int pid = fork();
if(pid < 0)
{
perror("fork");
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 1;
}
else if(pid > 0)
{
// 父进程
int i = 0;
while(1)
{
Poperation(semid, SEM_WR); // 申请写信号量,可写的位置--
strcpy(shmaddr+i, "a");
i++;
Voperation(semid, SEM_RD); // 释放读信号量,可读的内容++
sleep(1);
}
}
else
{
// 子进程
while(1)
{
Poperation(semid, SEM_RD); // 申请读信号量,可读的内容--
printf("%s\n",shmaddr);
Voperation(semid, SEM_WR); // 释放写信号量,可写的位置++
sleep(1);
}
}
#include
#include
#include
#include
#include
#include
#include
enum SEM
{
SEM_WR,
SEM_RD
};
union semun{
int val;
};
void Poperation(int semid, int semnum)
{
struct sembuf buf;
buf.sem_num = semnum;
buf.sem_op = -1;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
void Voperation(int semid, int semnum)
{
struct sembuf buf;
buf.sem_num = semnum;
buf.sem_op = 1;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
int main(){
key_t key = ftok(".", 100);
if (key < 0)
{
perror("ftok");
return 1;
}
// 创建信号量集合
int semid = semget(key, 2, IPC_CREAT | 0666);
if (semid < 0)
{
perror("semget");
return 1;
}
// 初始化信号量集合
union semun mysem;
mysem.val = 1;
semctl(semid, SEM_WR, SETVAL, mysem);
mysem.val = 0;
semctl(semid, SEM_RD, SETVAL, mysem);
// 新建共享内存
int shmid = shmget(key, 4*1024, IPC_CREAT | IPC_EXCL | 0666);
if (shmid < 0)
{
perror("shmget");
return 1;
}
// 把当前进场挂载到共享内存上
char* shmaddr = (char*)shmat(shmid, NULL, 0);
if(shmaddr == (void*)-1)
{
perror("shmat");
return 1;
}
// 创建子进程,让父子进程通过共享内存通信,通过信号量控制互斥
int pid = fork();
if(pid < 0)
{
perror("fork");
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 1;
}
else if(pid > 0)
{
// 父进程
int i = 0;
while(1)
{
Poperation(semid, SEM_WR); // 申请写信号量,可写的位置--
strcpy(shmaddr+i, "a");
i++;
Voperation(semid, SEM_RD); // 释放读信号量,可读的内容++
sleep(1);
}
}
else
{
// 子进程
while(1)
{
Poperation(semid, SEM_RD); // 申请读信号量,可读的内容--
printf("%s\n",shmaddr);
Voperation(semid, SEM_WR); // 释放写信号量,可写的位置++
sleep(1);
}
}
// 释放共享内存
shmctl(shmid, IPC_RMID, NULL);
// 删除信号量集合
semctl(semid, 0, IPC_RMID);
return 0;
}