一、信号量
1.作用:解决多个进程间的同步与互斥,可以用信号量。
2.信号量:本质是一个数字,这个数字可以进行加或者减的操作,对应的是P,V操作。消耗/获得信号量(-1),对应p操作;释放信号量(+1),对应v操作。信号量变为0后,其他进程想要消耗信号量就会被阻塞。
3.使用流程:
1)创建信号集;
2)初始化信号集中的每个信号值;
3)需要保护的临界代码前进行 P 操作;
4)执行完成临界代码后进行 V 操作;
5)不需要的话删除信号集。
4.相关函数
semget():信号量的创建或者打开
原型:int semget(key_t key, int nsems, int semflg);
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
参数:
key_t key:键值
int nsems:要创建的信号的个数 一般写1
int semflg:标志 IPC_CREAT | 0644
返回值:成功返回 信号量的id 失败 返回 -1
信号默认值为 0
semctl():设置/删除信号量
原型:int semctl(int semid, int semnum, int cmd, ...);
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
参数:
int semid:信号量的id
int semnum:信号量的下标 一般写0
int cmd:SETVAL:设置信号量
IPC_RMID:删除信号量
...:假如CMD为IPC_RMID,后边则没有参数
假如CMD为SETVAL时,设置信号量,SETVAL 后边跟几,就是设置几个信号量,需要传入一个共用体。
union semun {
int val; //给信号量赋的初值
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
返回值:成功,0 失败, -1
semop():消耗/释放一个信号量
原型:int semop(int semid, struct sembuf *sops, size_t nsops);
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
参数:
int semid:信号量的id
sops:对信号量的操作,封装有个结构体,
struct sembuf myops{
unsigned short sem_num; // 信号量的下标 ,一般填0
short sem_op; //要进行的操作
+1:V操作 释放一个信号量
-1:P操作 消耗一个信号量
short sem_flg; //0:表示申请不到信号量就阻塞
}; //自己定义的
size_t nsops:操作的信号量的个数, 一般写1
返回值:成功返回0 失败,-1
下面是一个练习的例子:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
int main()
{
struct sembuf myops;
key_t keyid;
int semid;
int pd,i;
pd = fork();
if(pd == 0)
{
keyid = ftok("/home/whs/002/7.5",5);
if(keyid<0)
{
perror("ftok");
return -1;
}
semid = semget(keyid,1,IPC_CREAT|0644); //信号量的创建
semctl(semid,0,SETVAL,1); //信号量的设置
myops.sem_num=0; //信号量的下标
myops.sem_op=-1; //要进行的操作
myops.sem_flg=0; //表示请不到信号就阻塞
semop(semid,&myops,1); //消耗一个信号量
for(i=0;i<5;i++)
{
printf("i=%d\n",i);
sleep(1);
}
myops.sem_op=1; //释放一个信号量
semop(semid,&myops,1);
}
if(pd > 0)
{
sleep(1);
keyid = ftok("/home/whs/002/7.5",5);
if(keyid<0)
{
perror("ftok");
return -1;
}
semid = semget(keyid,1,IPC_CREAT|0644);
myops.sem_num=0;
myops.sem_op=-1;
myops.sem_flg=0;
semop(semid,&myops,1); //消耗一个信号量
for(int j=0;j<5;j++)
{
printf("j=%d\n",j);
sleep(1);
}
myops.sem_op=1; //释放一个信号量
semop(semid,&myops,1);
}
semctl(semid,0,IPC_RMID); //删除信号量
return 0;
}
二、消息队列
1.特点
先进新出
一次性可以接收多条消息
当有进程读消息的时候,读取的最先进队列的
2.消息队列的消息的分类
可以是1类消息、2类消息、3类消息...........
3.相关函数
msgget():创建/打开一个消息队列
原型:int msgget(key_t key, int msgflg);
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
参数:
key_t key:键值
int msgflg:IPC_CREAT|0644
返回值:成功返回消息队列id 失败返回-1
msgsnd():向消息队列发送一个消息
原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
头文件:
#include <sys / types.h>
#include <sys / ipc.h>
#include <sys / msg.h>
参数:
msqid:消息队列的id
msgp:要发送的消息的结构体
struct msgbuf{
long mtype; //消息类型----必须是long类型
char mtext[100]; //消息正文
};---需要自己定义
msgsz:消息大小,不包括type.(sizeof(mybuf)-sizeof(long))
msgflg:一般赋0,阻塞方式
返回值:成功返回0 失败返回-1
msgrcv():从消息队列读取一个消息
原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
头文件:
#include <sys / types.h>
#include <sys / ipc.h>
#include <sys / msg.h>
参数:
msqid:消息队列的队列 ID
msgp:读取到的消息存放的地方的首地址(buf)
msgsz:消息大小,不包括type,不要以 null 结尾
msgtyp:消息类型, =0 ,表示读取不到就阻塞; >0 只接收指定类型的数据; <0 只接受小于等于其绝对值
msgflg:一般赋0,阻塞方式
返回值:接收成功返回0,失败返回-1
msgctl():消息队列的删除
原型:msgctl(int msgid,cmd ....);
参数:
msgid:消息队列的id
cmd:IPC_RMID
NULL结尾
下面是练习的代码:
//往消息队列里面写内容
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf{
long mtype;
char mtext[100];
}mybuf;//要发送的消息的结构体
int main()
{
key_t kid;
int mid;
kid =ftok("/home/whs/002/",6); //设置键值
mid = msgget(kid,IPC_CREAT|0644); //创建并打开一个消息队列
mybuf.mtype=1; //消息类型设置为1类消息
strcpy(mybuf.mtext,"hello!"); //消息里面要写到内容
msgsnd(mid,&mybuf,sizeof(mybuf)-sizeof(long),0); //向消息队列发送一个消息
return 0;
}
//从消息队列里面读取内容
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf{
long mtype;
char mtext[100];
}mybuf;
int main()
{
key_t kid;
char buf[120];
int mid;
kid =ftok("/home/whs/002/",6); //设置键值
mid = msgget(kid,IPC_CREAT|0644); //打开一个消息队列
msgrcv(mid,buf,sizeof(mybuf),1,0); //从消息队列读取一个消息
printf("buf=%s\n",(buf+8));
msgctl(mid,IPC_RMID,NULL); //删除消息队列
return 0;
}