先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
linux进程间IPC通信方式有三种,分别是共享内存、消息队列,以及信号灯。它
们都存在于内核空间,之前我们已经探讨过共享内存以及消息队列的实现方式:
linux进程间通信(五)----IPC篇----共享内存实现进程间通信
linux进程间通信(六)----消息队列篇
今天这篇文章来谈谈信号灯基本概念,以及实现进程间通信的步骤。
(1)概念
第一个问题:信号灯和POSX规范中的信号量之间的区别是什么呢?
posix信号量针对于单个信号量 seminit semwait sempost,Posix有名信号量,无名信号量。什么意思呢?就是POSIX规范中的信号量只能作用于一个信号量(当然这个信号量对应的资源可能不止一个),每个信号对应一个P操作和V操作;而System V信号灯是一个集合,它是信号量的集合,它含有多个信号量,它可以对多个信号灯同时进行P/V操作。
第二个问题:为什么需要信号灯,只有POSIX信号量不是足够了吗?
只有POSIX信号量是不够的,考虑这样一个场景,
#includde
#includde
#includde
1. 创建或者打开函数
原型: int semget(key_t key, int nsems, int semflag)
参数: key 和信号灯集关联的key值
nsems 信号灯集包含的信号灯数目
semflag 信号灯集的访问权限
返回值: 成功,信号灯ID,出错 -1
(3)关闭
原型: int semctl(int semid, int semnum, int cmd, ...union semun arg)
注意最后一个参数不是地址,可以有,可以没有
参数: semid 信号灯集id
semnum 要修改的信号灯集编号,删除操作时,这个值可以设置为任意值
cmd GETVAL 获取信号灯的值
SETVAL 设置信号灯的值
IPC_RMID 删除信号灯
union semun arg: union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
返回值: 成功,消息队列ID,出错 -1
(4)P/V操作函数
P/V操作函数 semop
原型: int semop(int semid, struct sembuf *opsptr, size_t nops)
参数: semid 信号灯集id
opsptr struct sembuf{
short sem_num; //要操作信号灯的编号
short sem_op; //0: 等待,直到信号灯的值变为0,1:资源释放,V操作,-1:分配资源,P操作
short sem_flg; //0: IPC_NOWAIT, SEM_UNDO
}
nops 要操作信号灯个数
返回值: 成功,消息队列ID,出错 -1
实现下面一个应用程序
父子进程通过system V信号灯同步对共享内存的读写
父进程从键盘输入字符串到共享内存
子进程删除字符串中的空格并打印
父进程输入quit后删除共享内存和信号灯集,程序结束
源代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define N 64
#define READ 0
#define WRITE 1
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
void init_sem(int semid, int s[], int n)
{
int i;
union semun myun;
for (i = 0; i < n; i++){
myun.val = s[i];
semctl(semid, i, SETVAL, myun);
}
}
void pv(int semid, int num, int op)
{
struct sembuf buf;
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
int main()
{
int shmid, semid, s[] = {
0, 1};
pid_t pid;
key_t key;
char *shmaddr;
key = ftok(".", 's');
if (key == -1){
perror("ftok");
exit(-1);
}
shmid = shmget(key, N, IPC_CREAT|0666);
if (shmid < 0) {
perror("shmid");
exit(-1);
}
semid = semget(key, 2, IPC_CREAT|0666);
if (semid < 0) {
perror("semget");
goto __ERROR1;
}
init_sem(semid, s, 2);
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == NULL) {
perror("shmaddr");
goto __ERROR2;
}
pid = fork();
if(pid < 0) {
perror("fork");
goto __ERROR2;
} else if (pid == 0) {
char *p, *q;
while(1) {
pv(semid, READ, -1);
p = q = shmaddr;
while (*q) {
if (*q != ' ') {
*p++ = *q;
}
q++;
}
*p = '\0';
printf("%s", shmaddr);
pv(semid, WRITE, 1);
}
} else {
while (1) {
pv(semid, WRITE, -1);
printf("input > ");
fgets(shmaddr, N, stdin);
if (strcmp(shmaddr, "quit\n") == 0) break;
pv(semid, READ, 1);
}
kill(pid, SIGUSR1);
}
__ERROR2:
semctl(semid, 0, IPC_RMID);
__ERROR1:
shmctl(shmid, IPC_RMID, NULL);
return 0;
}