系列文章目录
文章一: Linux—进程通信(一)—进程通信概述
文章二: Linux—进程通信(二)—管道
文章三: Linux—进程通信(三)—信号通信
文章四: Linux—进程通信(四)—IPC通信之共享内存
文章五: Linux—进程通信(五)—IPC通信之消息队列
文章六: Linux—进程通信(六)—IPC通信之信号灯
前言
本文主要讲解了IPC通信的对象之一-------信号灯
具体讲解了信号灯的各种操作函数
同时给出了用C语言编写的进程通信的实例
一、信号灯
1.信号灯是什么?
信号灯是多个信号量的一个集合,是在内核空间中被创建的用于进程间通信的一个IPC对象。
2.什么是信号量?
信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。
3.什么是P操作和V操作?
P(S):
1.将信号量S的值减1,即S=S-1;
2.如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):
1.将信号量S的值加1,即S=S+1;
2.如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
PV操作的意义:
我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。
二、信号灯的使用
1.信号灯的操作函数
信号灯的创建函数
函数原型:
int semget(key_t key,int nsems,int semflg);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
函数功能:
在内核空间中创建一个信号灯
函数参数:
1.key:和信号灯关联的key值
2.nsems:信号灯中包含的信号量的个数
3.semflg:信号灯集的访问权限
函数返回值:
1.成功返回信号灯集的ID
2.出错返回-1
信号灯创建函数的实例
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int main()
{
int semid;
semid=semget(IPC_PRIVATE,3,0777);
if(semid<0)
{
printf("create semaphore failure\n");
return -1;
}
printf("create semaphore success shmid=%d\n",semid);
system("ipcs -s");
return 0;
}
执行结果:
信号灯的控制函数
函数原型:
int semctl(int semid,int semnum,int cmd,...union semun arg);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
函数功能:
1.将内核空间中的信号灯删除,释放空间
2.获取和设置信号灯集中具体编号信号量的值
函数参数:
1.msqid:信号灯的ID
2.semnum:要修改的信号量在信号灯集中的编号
3.cmd:
(1)GETVAL:获取该编号信号量的值
(2)SETVAL:设置该编号信号量的值
(3)IPC_RMID:从系统中删除信号灯集合
4.union semun:
(1)当cmd选择了IPC_RMID后,可以是NULL也可以是
(2)当cmd选择了其它两个参数,该参数是
union semun的结构:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf
};
函数返回值:
1.成功返回0
2.失败返回-1
信号灯的操作函数(P/V操作)
函数原型
int semop(int semid,struct sembuf *opsptr,size_t nops);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
函数参数:
1.semid:信号灯集的ID
2.struct sembuf:操作信号灯信号量的信息
3.nops:要操作的信号灯集信号量的个数
struct sembuf的数据结构:
struct sembuf
{
short sem_num;//要操作信号量的编号
short sem_op;
//0:等待,直到信号量的值变为0
//1:释放资源,V操作
//-1:分配资源,P操作
short sem_flg;
//0:阻塞
//IPC_NOWAIT:非阻塞
//SEM_UNDO:撤销操作
}
函数返回值:
成功返回0,出错返回-1
三、用信号灯实现两个无亲缘关系的进程之间的先后运行控制
进程1后运行
进程1的C语言代码:
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int main()
{
int i;
union semun mysemun;
struct sembuf mysembuf;
int semid;
int key;
key=ftok("./a.c","a");
if(key<0)
{
printf("create key failure\n");
return -1;
}
printf("create key success\n");
semid=semget(key,3,IPC_CREAT|0777);
if(semid<0)
{
printf("create semaphore failure\n");
return -1;
}
printf("create semaphore success shmid=%d\n",semid);
system("ipcs -s");
mysembuf.sem_num=0;
mysembuf.sem_flg=0;
for(i=0;i<10;i++)
{
usleep(100);
printf("this is main fun i=%d\n",i);
}
mysembuf.sem_op=1;
semop(semid,&mysembuf,1);
while(1);
return 0;
}
进程2先运行
进程2的C语言代码:
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int main()
{
int i;
union semun mysemun;
struct sembuf mysembuf;
int semid;
int key;
key=ftok("./a.c","a");
if(key<0)
{
printf("create key failure\n");
return -1;
}
printf("create key success\n");
semid=semget(key,3,IPC_CREAT|0777);
if(semid<0)
{
printf("create semaphore failure\n");
return -1;
}
printf("create semaphore success shmid=%d\n",semid);
system("ipcs -s");
mysemun.val=0;
semctl(semid,0,SETVAL,mysemun);
mysembuf.sem_num=0;
mysembuf.sem_flg=0;
mysembuf.sem_op=-1;
semop(semid,&mysembuf,1);
for(i=0;i<10;i++)
{
usleep(100);
printf("this is main fun i=%d\n",i);
}
while(1)
return 0;
}
注意:
运行时先运行进程2,再运行进程1可以看到效果。
因为对信号灯的初始化在进程2中,同时进程2会等待进程1运行完,故可以看到效果
执行效果:
虽然进程2先打开,但是进程1先打印语句,进程2后打印语句
总结
到这里关于进程通信的系列文章已经结束。
由于笔者是新手,介绍的不对不妥的地方请读者包含并指正。