IPC进程间通信_system V
消息队列
共享内存
信号灯
查看相关的命令
ipcs 查看所有的信息(消息队列,共享内存,信号量)
ipcs -q 消息队列(queue)
ipcs -m 共享内存(memory)
ipcs -s 信号量(信号灯)(semphore)
删除相关的命令
ipcrm -q/-m/-s ID 删除
注:这里的ID并不是key
1消息队列
1.1消息队列的原理图
1.2消息队列API(ftok|msgget|msgsnd|msgrcv|msgctl)
1.获得key值
第一种:
key_t ftok(const char *pathname, int proj_id);
参数:
@pathname:已经存在的文件路径
@proj_id :获取这个整数的低8bit
返回值:成功返回 key值,失败返回-1
注:(key = proj_id(8bit),st_dev(8bit),inode(16bit))
第二种:
将key值指定为IPC_PRIVATE ,当IPC对象在亲缘关系进程通信的时候
2.创建IPC对象
int msgget(key_t key, int msgflg);
功能:创建消息队列
参数:
@key :IPC_PRIVATE 或 ftok
@msgflg :创建共享内存的标志位
IPC_CREAT | 0666 //如果消息队列不存在就创建,如果存在就返回消息队列的ID
或
IPC_CREAT | IPC_EXCL | 0666 //如果消息队列不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1
注意:
如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID
3.发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:发送消息
参数:
@msqid :消息队列ID
@msgp :需要发送的消息存放的地址
@msgsz :消息正文的大小
@msgflg :发消息的标志位
0:阻塞的方式发送
IPC_NOWAIT:非阻塞方式调用
返回值:成功返回0,失败返回-1 (队列的大小限制MSGMNB 16384)
消息结构体定义:
typedef struct{
long msg_type; //消息类型必须在第一个位置,
char mtxt[1024];
...
}msg_t;
正文大小:sizeof(msg_t) - sizeof(long)
4.接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:接收消息
参数:
@msqid:消息队列ID
@msgp :存放接收到的消息
@msgsz:正文大小
@msgtyp:消息类型
0: 总是从消息队列中提取第一个消息
>0:指向消息的类型接收消息,如果类型不匹配,详细依然在消息队列中存放
<0:对消息的类型取绝对值
-5--->|-5|-->5-->接收消息类型小于5的消息(1-2-3-4-1-3)
队列 -1-2-3-4-1-3-100-200--
@msgflg:
0:阻塞的方式接收
IPC_NOWAIT:非阻塞方式调用
返回值:成功返回 接收消息正文大小,失败返回-1
5.消息队列的控制
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:消息队列的控制
参数:
@msgqid:消息队列ID
@cmd : 控制方式
IPC_RMID(删除消息队列)
IPC_SET(设置消息队列的属性信息)
IPC_STAT(获取消息队列属性信息)
@buf :存放消息队列属性 (如果cmd填写为IPC_RMID这个参数缺省)
返回值:成功返回0,失败返回-1
1.3消息队列的实例
1.3.1相同的消息类型收发实例
01snd.c
#include <head.h>
typedef struct{
long msg_type; //消息类型必须在第一个位置,
char mtxt[1024]; //消息的正文
}msg_t;
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
int main(int argc,const char * argv[])
{
key_t key;
int msgid; //消息队列的id;
msg_t msg = {
.msg_type = 100,
};
//1.获取一个key值
if((key = ftok("/home/linux",'t'))==-1)
PRINT_ERR("ftok get key error");
//2.创建消息队列
if((msgid = msgget(key, IPC_CREAT|0664))==-1)
PRINT_ERR("msgget error");
//3.向消息队列中发消息
while(1){
//从终端向消息结构体的正文中输入信息
printf("input > ");
fgets(msg.mtxt,sizeof(msg.mtxt),stdin);
msg.mtxt[strlen(msg.mtxt)-1]='\0';
//将消息发到消息队列中
//msgid:消息队列号
//msg:消息首地址
//MSGSIZE:消息正文的大小
//0:阻塞方式发送,如果消息队列满了,就阻塞等待
if(msgsnd(msgid, &msg,MSGSIZE, 0)==-1)
PRINT_ERR("msgsnd error");
}
return 0;
}
02rcv.c
#include <head.h>
typedef struct{
long msg_type; //消息类型必须在第一个位置,
char mtxt[1024]; //消息的正文
}msg_t;
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
int main(int argc,const char * argv[])
{
key_t key;
int msgid; //消息队列的id;
msg_t msg;
//1.获取一个key值
if((key = ftok("/home/linux",'t'))==-1)
PRINT_ERR("ftok get key error");
//2.创建消息队列(不会再次创建)
if((msgid = msgget(key, IPC_CREAT|0664))==-1)
PRINT_ERR("msgget error");
//3.向消息队列中取消息
while(1){
//0:每次从消息队列中取第一个消息
//0:阻塞方式接收消息
if(msgrcv(msgid,&msg,MSGSIZE,0,0)==-1)
PRINT_ERR("msgsnd error");
printf("消息类型=%ld,消息正文=%s\n",msg.msg_type,msg.mtxt);
}
return 0;
}
1.3.2不同的消息类型收发实例
01snd.c
#include <head.h>
typedef struct
{
long id; //类型,学号
char name[20]; //学生名字
int score; //学生的成绩
} msg_t;
#define MSGSIZE (sizeof(msg_t) - sizeof(long))
int main(int argc, const char *argv[])
{
key_t key;
int msgid; //消息队列的id;
struct msqid_ds buf;
// 1.获取一个key值
if ((key = ftok("/home/linux", 't')) == -1)
PRINT_ERR("ftok get key error");
// 2.创建消息队列
if ((msgid = msgget(key, IPC_CREAT | 0664)) == -1)
PRINT_ERR("msgget error");
msg_t msg = {100,"zhangsan",88};
if (msgsnd(msgid, &msg, MSGSIZE, 0) == -1)
PRINT_ERR("msgsnd error");
msg_t msg1 = {101,"lisi",20};
if (msgsnd(msgid, &msg1, MSGSIZE, 0) == -1)
PRINT_ERR("msgsnd error");
msg_t msg2 = {102,"wangwu",100};
if (msgsnd(msgid, &msg2, MSGSIZE, 0) == -1)
PRINT_ERR("msgsnd error");
return 0;
}
02rcv.c
#include <head.h>
typedef struct
{
long id; //类型,学号
char name[20]; //学生名字
int score; //学生的成绩
} msg_t;
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
int main(int argc,const char * argv[])
{
key_t key;
int msgid; //消息队列的id;
msg_t msg;
//1.获取一个key值
if((key = ftok("/home/linux",'t'))==-1)
PRINT_ERR("ftok get key error");
//2.创建消息队列(不会再次创建)
if((msgid = msgget(key, IPC_CREAT|0664))==-1)
PRINT_ERR("msgget error");
int id;
//3.向消息队列中取消息
while(1){
printf("请输入学号> ");
scanf("%d",&id);
if(msgrcv(msgid,&msg,MSGSIZE,id,0)==-1)
PRINT_ERR("msgsnd error");
printf("id=%ld,name=%s,score=%d\n",
msg.id,msg.name,msg.score);
}
return 0;
}
1.3.3使用IPC_PRIVATE实现亲缘进程消息队列通信
#include <head.h>
typedef struct
{
long id; //类型,学号
char name[20]; //学生名字
int score; //学生的成绩
} msg_t;
#define MSGSIZE (sizeof(msg_t) - sizeof(long))
int main(int argc, const char *argv[])
{
int msgid; //消息队列的id;
pid_t pid;
// 1.创建消息队列
if ((msgid = msgget(IPC_PRIVATE, IPC_CREAT | 0664)) == -1)
PRINT_ERR("msgget error");
pid = fork();
if (pid < 0)
{
PRINT_ERR("fork error");
}
else if (pid == 0)
{
msg_t msg = {100, "zhangsan", 88};
if (msgsnd(msgid, &msg, MSGSIZE, 0) == -1)
PRINT_ERR("msgsnd error");
exit(EXIT_SUCCESS);
}
else
{
msg_t msg;
memset(&msg, 0, sizeof(msg));
if (msgrcv(msgid, &msg, MSGSIZE, 100, 0) == -1)
PRINT_ERR("msgsnd error");
printf("id=%ld,name=%s,score=%d\n",
msg.id, msg.name, msg.score);
wait(NULL);
}
return 0;
}
1.4msgctl函数的使用
1.4.1消息队列控制函数的使用(IPC_STAT)
struct msqid_ds
{
struct ipc_perm msg_perm; //消息队列权限的结构体
__syscall_ulong_t __msg_cbytes; //当前消息队列中字节个数
msgqnum_t msg_qnum; //当前消息队列中消息的个数
msglen_t msg_qbytes; //消息队列能容纳的消息的个数(16384)
__pid_t msg_lspid; //最后一次发消息进程号
__pid_t msg_lrpid; //最后一次收消息进程号
};
struct ipc_perm
{
__key_t __key; //ftok获取的key
__uid_t uid; //所属用户的ID
__gid_t gid; //所属组的ID
__uid_t cuid; //创建消息队列用户的ID
__gid_t cgid; //创建消息队列的组的ID
unsigned short int mode; //消息队列的权限
unsigned short int __seq; //消息队列号msgid
};
#include <head.h>
typedef struct{
long msg_type; //消息类型必须在第一个位置,
char mtxt[1024]; //消息的正文
}msg_t;
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
int main(int argc,const char * argv[])
{
key_t key;
int msgid; //消息队列的id;
struct msqid_ds buf;
msg_t msg = {
.msg_type = 100,
};
//1.获取一个key值
if((key = ftok("/home/linux",'t'))==-1)
PRINT_ERR("ftok get key error");
//2.创建消息队列
if((msgid = msgget(key, IPC_CREAT|0664))==-1)
PRINT_ERR("msgget error");
//3.向消息队列中发消息
while(1){
//从终端向消息结构体的正文中输入信息
printf("input > ");
fgets(msg.mtxt,sizeof(msg.mtxt),stdin);
msg.mtxt[strlen(msg.mtxt)-1]='\0';
//将消息发到消息队列中
//msgid:消息队列号
//msg:消息首地址
//MSGSIZE:消息正文的大小
//0:阻塞方式发送,如果消息队列满了,就阻塞等待
if(msgsnd(msgid, &msg,MSGSIZE, 0)==-1)
PRINT_ERR("msgsnd error");
msgctl(msgid,IPC_STAT,&buf);
printf("cbytes=%ld\n",buf.__msg_cbytes);//当前消息队列中字节个数
printf("qnum =%ld\n",buf.msg_qnum);//消息的个数
printf("qbytes=%ld\n",buf.msg_qbytes); //消息队列能容纳的消息的个数(16384)
printf("pid = %d\n",buf.msg_lspid); //最后一次发消息的进程号
printf("mode = %#o\n",buf.msg_perm.mode);//消息队列的权限
printf("key = %#x\n",buf.msg_perm.__key);//ftok函数得到的key
printf("msgid = %u\n",buf.msg_perm.__seq);//暂未使用
printf("msgid = %u\n",msgid);//消息队列序号
}
return 0;
}
1.4.2消息队列控制函数的使用(IPC_RMID)
msgctl(msgid,IPC_RMID,NULL); //删除消息队列
或者
ipcrm -q msgid //删除消息队列
#include <head.h>
typedef struct{
long msg_type; //消息类型必须在第一个位置,
char mtxt[1024]; //消息的正文
}msg_t;
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
int main(int argc,const char * argv[])
{
key_t key;
int msgid; //消息队列的id;
struct msqid_ds buf;
msg_t msg = {
.msg_type = 100,
};
//1.获取一个key值
if((key = ftok("/home/linux",'t'))==-1)
PRINT_ERR("ftok get key error");
//2.创建消息队列
if((msgid = msgget(key, IPC_CREAT|0664))==-1)
PRINT_ERR("msgget error");
//3.向消息队列中发消息
while(1){
//从终端向消息结构体的正文中输入信息
printf("input > ");
fgets(msg.mtxt,sizeof(msg.mtxt),stdin);
msg.mtxt[strlen(msg.mtxt)-1]='\0';
//将消息发到消息队列中
//msgid:消息队列号
//msg:消息首地址
//MSGSIZE:消息正文的大小
//0:阻塞方式发送,如果消息队列满了,就阻塞等待
if(msgsnd(msgid, &msg,MSGSIZE, 0)==-1)
PRINT_ERR("msgsnd error");
msgctl(msgid,IPC_STAT,&buf);
printf("cbytes=%ld\n",buf.__msg_cbytes);
printf("qnum =%ld\n",buf.msg_qnum);
printf("qbytes=%ld\n",buf.msg_qbytes);
printf("pid = %d\n",buf.msg_lspid);
printf("mode = %#o\n",buf.msg_perm.mode);
printf("key = %#x\n",buf.msg_perm.__key);
printf("msgid = %u\n",buf.msg_perm.__seq);
printf("msgid = %u\n",msgid);
if(strcmp(msg.mtxt,"quit")==0)break;
}
msgctl(msgid,IPC_RMID,NULL); //删除消息队列
return 0;
}
2共享内存
2.1共享内存的原理
2.2共享内存的API(ftok|shmget|shmat|shmdt|shmctl)
(1)创建共享内存
int shmget(key_t key, size_t size, int shmflg);
功能:获取共享内存段的ID
参数:
@key :IPC_PRIVATE 或 ftok()
@size:申请的共享内存段大小 [4k的倍数]
@shmflg:创建消息队列的标志位
IPC_CREAT | 0666 //如果共享内存不存在就创建,如果存在就返回共享内存的ID
或
IPC_CREAT | IPC_EXCL | 0666 //如果共享内存不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1
(2)映射内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存到用户空间
参数:
@shmid :共享内存段ID
@shmaddr:映射到的地址
NULL:系统自动完成映射
@shmflg :共享内存操作的标志位
SHM_RDONLY:只读
0:读写
返回值:成功返回映射后的地址,失败返回(void *)-1
(3)撤销映射
int shmdt(const void *shmaddr);
功能:撤销映射
参数:
@shmaddr 共享内存映射的地址
返回值:成功返回0,失败返回-1置位错误码
注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射
(4)共享内存的控制函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmctl(shmid,IPC_RMID,NULL);
功能:根据命令控制共享内存
参数:
@shmid :共享内存的ID
@cmd :控制方式
IPC_STAT[获取属性],
IPC_SET[设置属性],
IPC_RMID[删除IPC对象]
@buf :存放消息队列属性 (如果cmd填写为IPC_RMID这个参数缺省)
返回值:成功返回0,失败返回 -1
注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。
只有当共享内存映射次数为0,才会删除共享内存对象
2.3共享内存使用实例
01write.c
#include <head.h>
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
key_t key;
int shmid;
char *addr=NULL;
//1.获取key值
if((key=ftok("/home/linux",'a'))==-1)
PRINT_ERR("ftok error");
//2.创建共享内存
if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
PRINT_ERR("create share memory error");
//3.将共享内存映射到这个进程空间
addr=shmat(shmid,NULL,0);
if(addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n",addr);
//4.向共享内存中写数据
printf("shm input > ");
fgets(addr,SHMSIZE,stdin);
addr[strlen(addr)-1]='\0';
//敲回车结束程序
getchar();
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
02read.c
#include <head.h>
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
key_t key;
int shmid;
char *addr=NULL;
//1.获取key值
if((key=ftok("/home/linux",'a'))==-1)
PRINT_ERR("ftok error");
//2.创建共享内存
if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
PRINT_ERR("create share memory error");
//3.将共享内存映射到这个进程空间
addr=shmat(shmid,NULL,0);
if(addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n",addr);
//4.从共享内存中读取数据
printf("read shm data=%s\n",addr);
//敲回车结束程序
getchar();
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
问:上述的程序能够保证读写的顺序?如果没有执行写,先向执行读的现象?
答:上述的共享内存在没有执行写的时候,执行读是不会阻塞的,所以如果向
看到现象就必须先执行写,在执行读,上述的代码遵从生产者消费者模型,必
须同步后才能正确看到效果。使用IPC通信中的信号灯机制实现同步。
问:共享内存的数据读取后还存在吗?
答:存在,直到下一次写覆盖掉,或者删除共享内存这些数据才消失
2.4shmctl函数的使用
2.4.1共享内存控制函数的使用(IPC_STAT)
struct shmid_ds
{
struct ipc_perm shm_perm; //共享内存权限
size_t shm_segsz; //共享内存大小,单位是字节
__time_t shm_atime; //最后一次,shmat的时间
__pid_t shm_cpid; //创建者的pid
__pid_t shm_lpid; //最后一个操作共享内存的pid
shmatt_t shm_nattch; //共享内存的链接数
};
struct ipc_perm
{
__key_t __key; //ftok获取的key
__uid_t uid; //所属用户的ID
__gid_t gid; //所属组的ID
__uid_t cuid; //创建消息队列用户的ID
__gid_t cgid; //创建消息队列的组的ID
unsigned short int mode; //消息队列的权限
};
#include <head.h>
#define SHMSIZE 4096
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *addr = NULL;
// 1.获取key值
if ((key = ftok("/home/linux", 'a')) == -1)
PRINT_ERR("ftok error");
// 2.创建共享内存
if ((shmid = shmget(key, SHMSIZE, IPC_CREAT | 0664)) == -1)
PRINT_ERR("create share memory error");
// 3.将共享内存映射到这个进程空间
addr = shmat(shmid, NULL, 0);
if (addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n", addr);
// 4.向共享内存中写数据
printf("shm input > ");
fgets(addr, SHMSIZE, stdin);
addr[strlen(addr) - 1] = '\0';
struct shmid_ds buf;
shmctl(shmid, IPC_STAT, &buf);
printf("size = %ld\n", buf.shm_segsz);
printf("nattch= %ld\n", buf.shm_nattch);
printf("create pid = %d\n", buf.shm_cpid);
printf("last op pid = %d\n", buf.shm_lpid);
printf("mode = %#o\n", buf.shm_perm.mode);
printf("key = %#x\n", buf.shm_perm.__key);
printf("shmid = %u\n", shmid);
//敲回车结束程序
getchar();
//删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
2.4.2共享内存控制函数的使用(IPC_RMID)
shmctl(shmid,IPC_RMID,NULL); //删除共享内存
或者
ipcrm -m shmid //删除共享内存
#include <head.h>
#define SHMSIZE 4096
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *addr = NULL;
// 1.获取key值
if ((key = ftok("/home/linux", 'a')) == -1)
PRINT_ERR("ftok error");
// 2.创建共享内存
if ((shmid = shmget(key, SHMSIZE, IPC_CREAT | 0664)) == -1)
PRINT_ERR("create share memory error");
// 3.将共享内存映射到这个进程空间
addr = shmat(shmid, NULL, 0);
if (addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n", addr);
// 4.向共享内存中写数据
printf("shm input > ");
fgets(addr, SHMSIZE, stdin);
addr[strlen(addr) - 1] = '\0';
struct shmid_ds buf;
shmctl(shmid, IPC_STAT, &buf);
printf("size = %ld\n", buf.shm_segsz);
printf("nattch= %ld\n", buf.shm_nattch);
printf("create pid = %d\n", buf.shm_cpid);
printf("last op pid = %d\n", buf.shm_lpid);
printf("mode = %#o\n", buf.shm_perm.mode);
printf("key = %#x\n", buf.shm_perm.__key);
printf("shmid = %u\n", shmid);
//敲回车结束程序
getchar();
//删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
3.信号量
3.1信号量的概念
信号量:信号量是实现进程间同步的方式,这个信号量又叫信号灯集。在一个信号量中可以包含很多的信号灯,信号灯之间相互不干扰。
3.2信号量的API(ftok|semget|semop|semctl)
POSIX 线程中的同步用的是无名信号量
进程间的同步使用的是IPC 对象[信号灯集]
信号灯集:信号灯集合,每一个信号灯都可以用来表示一类资源,其值表示资源的个数
(1)创建信号灯集
int semget(key_t key, int nsems, int semflg);
功能:创建信号灯集
参数:
@key IPC_PRIVATE , ftok()
@nsems 信号灯集中信号灯的个数
@semflg IPC_CREAT | 0666,IPC_CREAT | IPC_EXCL
返回值:成功返回ID,失败返回-1
(2)初始化信号灯集中信号灯的值
int semctl(int semid, int semnum, int cmd, ...);
参数:
@semid :信号灯集的ID
@semnum :信号灯的编号[编号从0开始]
@cmd :SETVAL[设置信号灯的值] ,GETVAL(获取信号灯的值),IPC_RMID[删除信号灯集]
@... :联合体 union semun sem;
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) */
};
返回值:成功返回0,失败返回-1
(3)信号灯的操作(PV)
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:完成PV操作
参数:
@semid :信号灯集的ID
@sops :操作方式结构体首地址
struct sembuf{
unsigned short sem_num; //信号灯的编号
short sem_op; //操作方式
-1 :P操作,申请资源
1 :V操作,释放资源
short sem_flg; //操作的标志位
IPC_NOWAIT:非阻塞方式
0 :阻塞方式操作
};
@nsops :操作信号灯的个数
返回值:成功返回0,失败返回-1
3.3将上述的API进程函数封装
sem.c
#include <head.h>
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) */
};
int init_semnum_value(int semid, int semnum, int value)
{
union semun sem;
sem.val = value;
if (semctl(semid, semnum, SETVAL, sem) < 0)
PRINT_ERR("semctl init value error");
return 0;
}
int sem_init(void)
{
key_t key;
int semid;
// 1.获取key值
if ((key = ftok("/home/linux", 'w')) == -1)
PRINT_ERR("ftok error");
// 2.创建信号量
//如果信号量不存在就创建信号量,创建号信号量后执行else语句对
//信号灯的初值进行初始化
//如果信号量已存在,IPC_EXCL这个就会让semget报EEXIST,如果
//信号灯已存在,直接获取信号灯semid,否则打印错误。如果信号
//量已存在就不再对信号量中的信号灯的初值做初始化了
if ((semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0664)) == -1)
{
if(errno == EEXIST){
semid = semget(key, 2, IPC_CREAT | 0664);
}else{
PRINT_ERR("create sem error");
}
}
else
{
// 3.初始化信号量
//初始化信号灯集中的两个信号灯的初值
// 0号灯的初值为0
// 1号灯的初值为1
init_semnum_value(semid, 0, 0);
init_semnum_value(semid, 1, 1);
}
return semid;
}
// P操作代表申请资源
int P(int semid, int semnum)
{
struct sembuf buf;
buf.sem_num = semnum;
buf.sem_op = -1; //申请资源
buf.sem_flg = 0; //阻塞方式申请
if (semop(semid, &buf, 1) < 0)
PRINT_ERR("P error");
return 0; //成功返回0
}
// V操作代表释放资源
int V(int semid, int semnum)
{
struct sembuf buf;
buf.sem_num = semnum;
buf.sem_op = 1; //释放资源
buf.sem_flg = 0; //阻塞方式申请
if (semop(semid, &buf, 1) < 0)
PRINT_ERR("V error");
return 0; //成功返回0
}
int sem_del(int semid)
{
// 1.删除信号灯集合
if (semctl(semid, 0, IPC_RMID, NULL) == -1)
PRINT_ERR("delete sem error");
return 0;
}
sem.h
#ifndef __SEM_H__
#define __SEM_H__
int sem_init(void);
int P(int semid, int semnum);
int V(int semid, int semnum);
int sem_del(int semid);
#endif
3.4实例(使用信号灯集完成AB进程共享内存的同步过程)
01write.c
#include <head.h>
#include "sem.h"
#define SHMSIZE 4096
int main(int argc, const char *argv[])
{
key_t key;
int shmid, semid;
char *addr = NULL;
// 1.创建信号量
if ((semid = sem_init()) == -1)
PRINT_ERR("sem create and init error");
// 1.获取key值
if ((key = ftok("/home/linux", 'a')) == -1)
PRINT_ERR("ftok error");
// 2.创建共享内存
if ((shmid = shmget(key, SHMSIZE, IPC_CREAT | 0664)) == -1)
PRINT_ERR("create share memory error");
// 3.将共享内存映射到这个进程空间
addr = shmat(shmid, NULL, 0);
if (addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n", addr);
// 4.向共享内存中写数据
while (1)
{
P(semid,1);
printf("shm input > ");
fgets(addr, SHMSIZE, stdin);
addr[strlen(addr) - 1] = '\0';
V(semid,0);
if(strcmp(addr,"quit")==0)break;
}
//删除共享内存
shmctl(shmid, IPC_RMID, NULL);
sem_del(semid);
return 0;
}
02read.c
#include <head.h>
#include "sem.h"
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
key_t key;
int shmid, semid;
char *addr = NULL;
// 1.创建信号量
if ((semid = sem_init()) == -1)
PRINT_ERR("sem create and init error");
//1.获取key值
if((key=ftok("/home/linux",'a'))==-1)
PRINT_ERR("ftok error");
//2.创建共享内存
if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
PRINT_ERR("create share memory error");
//3.将共享内存映射到这个进程空间
addr=shmat(shmid,NULL,0);
if(addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n",addr);
//4.从共享内存中读取数据
while(1){
P(semid,0);
printf("read shm data=%s\n",addr);
if(strcmp(addr,"quit")==0)break;
V(semid,1);
}
return 0;
}
编译过程:
gcc 01write.c sem.c -o write
gcc 02read.c sem.c -o read
执行过程
shmctl(shmid, IPC_RMID, NULL);
sem_del(semid);
return 0;
}
### 02read.c
```c
#include <head.h>
#include "sem.h"
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
key_t key;
int shmid, semid;
char *addr = NULL;
// 1.创建信号量
if ((semid = sem_init()) == -1)
PRINT_ERR("sem create and init error");
//1.获取key值
if((key=ftok("/home/linux",'a'))==-1)
PRINT_ERR("ftok error");
//2.创建共享内存
if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
PRINT_ERR("create share memory error");
//3.将共享内存映射到这个进程空间
addr=shmat(shmid,NULL,0);
if(addr == (void *)(-1))
PRINT_ERR("shmat error");
printf("shmat addr = %p\n",addr);
//4.从共享内存中读取数据
while(1){
P(semid,0);
printf("read shm data=%s\n",addr);
if(strcmp(addr,"quit")==0)break;
V(semid,1);
}
return 0;
}
编译过程:
gcc 01write.c sem.c -o write
gcc 02read.c sem.c -o read
执行过程