第四十讲 system-v
文章目录
一、system-v 消息队列
1、system-v ipc特点
- 独立于进程
- 没有文件名和描述符
- IPC 对象具有 key 和 ID
2、消息队列用法
- 定义一个唯一的key(使用 ftok 函数)
- 构造消息对象(msgget)
- 发送特点类型消息(msgsnd)
- 接收特点消息(msgrcv)
- 控制消息队列(msgctl)
3、常用函数
函数 | 功能 | 参数 | 返回值 | 头文件 |
---|---|---|---|---|
key_t ftok (const char *path, int proj_id) | 获取一个key | path:路径 proj_id:一个整数id | 成功:合法键值 失败:-1 | sys/types.h sys/ipc.h sys/msg.h |
int msgget (key_t key, int msgflg) | 获取消息队列id | key:消息队列键值 msgflg: IPC_CREAT:消息队列不存在,则创建 mode:访问权限 | 成功:消息队列id 失败:-1 | |
int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg) | 发送消息到消息队列 | msqid:消息队列id msgp:消息缓存区(典型的消息结构:long类型的消息类型加上消息) msgsz:消息大小 msgflg:0:阻塞发送 IPC_NOWAIT:非阻塞发送 | 成功:0 失败:-1 | |
size_t msgrcv(int msqid, void *msgp, size_t msgsz,long msgtyp, int msgflg) | 从消息队列接收消息 | msqid:消息队列id msgp:消息缓存区 msgsz:消息正文字节数 msgtyp:接收的消息表示 msgflg:IPC_NOWAIT:非阻塞读取 MSG_NOERROR:截断消息(当消息长度小于指定长度时会报错,此时使用这个就不会摆错了) 0:阻塞读取 | 成功:0 失败:-1 | |
int msgctl(int msgqid, int cmd, struct maqid_ds *buf) | 控制消息队列 | msgqid:消息队列id cmd:IPC_STAT:获取消息队列属性信息 IPC_SET:设置消息队列属性 IPC_RMID:删除消息队列 buf:消息队列管理结构体 | 成功:0 失败:-1 |
4、示例
读取函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MSG_SIZE 20
struct myMessage
{
/* data */
long msgType;
char msgBuf[MSG_SIZE];
};
int main(char *argc, char **argv)
{
int id;
key_t key;
struct myMessage msg;
/*获取键值*/
key = ftok("./", 11);
if(key == -1)
{
printf("Get key failed!\r\n");
exit(-1);
}
/*获取消息队列id*/
id = msgget(key, IPC_CREAT | 0666);
if(id == -1)
{
printf("Get msg queue id failed!\e\n");
exit(-1);
}
memset(&msg, '\0', sizeof(msg));
if(msgrcv(id, &msg, MSG_SIZE, 0, 0) < 0)
{
printf("Message receive failed!\r\n");
exit(-1);
}
else
{
printf("Receive:%ld %s", msg.msgType, msg.msgBuf);
if(msgctl(id, IPC_RMID, NULL) < 0)
{
printf("Message queue delete failed!\r\n");
exit(-1);
}
else
{
printf("Message queue delete success!\r\n");
}
}
return 0;
}
写入函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MSG_SIZE 20
struct myMessage
{
/* data */
long msgType;
char msgBuf[MSG_SIZE];
};
int main(char *argc, char **argv)
{
int id;
key_t key;
struct myMessage msg;
/*获取键值*/
key = ftok("./", 11);
if(key == -1)
{
printf("Get key failed!\r\n");
exit(-1);
}
/*获取消息队列id*/
id = msgget(key, IPC_CREAT | 0666);
if(id == -1)
{
printf("Get msg queue id failed!\e\n");
exit(-1);
}
msg.msgType = getpid();
memcpy(msg.msgBuf, "Hello!\r\n", strlen("Hello!\r\n"));
if(msgsnd(id, &msg, strlen(msg.msgBuf), 0) < 0)
{
printf("Message send failed!\r\n");
exit(-1);
}
}
5、效果
发送程序发送完直接停止运行
接收程序
eue_rcv
Receive:3365 Hello!
����"VMessage queue delete success!
二、system-v 信号量
1、介绍
system-v 信号量是用来记录系统可以访问的共享资源的次数。包括硬件设备、元件、共享内存等。使用信号量对共享资源进行保护时,在访问共享资源时,需要先去判断信号量的值。如果信号量的值大于0则可以访问共享资源,访问时对信号量进行 -1操作(take),访问完成惠普对信号量进行 +1 操作(give)。当信号量减少到0的时候,就不能访问共享资源了。
访问共享资源方式:
- 互斥访问:如果共享资源已经被访问了,那么其他的就不能访问共享资源了
- 同步访问:在互斥访问的基础上增加了访问顺序
2、信号量的用法
- 定义一个唯一的 key(ftok)
- 构造信号量(semget)
- 初始化信号量(semctl)
- 定义信号量进行p/v操作(semop)
- 删除信号量(semctl)
3、常用函数
函数 | 功能 | 参数 | 返回值 | 头文件 |
---|---|---|---|---|
int semget(key_t key, int nsems, int semflg) | 获取信号量id | key:键值 nsems:信号量数量 semflg:IPC_CREAT:信号量不存在的情况下创建 mode:信号量权限 | 成功:信号量id 失败:-1 | signal.h |
int semctl(int semid, int semnum, int cmd, union semun arg) | 获取或设置信号量的相关属性 | semid:信号量id semnum:信号量编号 cmd: IPC_STAT:获取信号量属性信息 IPC_SET:设置信号量属性 IPC_RMID:删除信号量 IPC_SETVAL:设置信号量的值 arg: union semnum{int val;struct semid_ds buf} | 成功:由cmd类型决定,一般为0 失败:-1 | |
int semop(int semid, struct sembuf *sops, size_t nsops) | 对信号量进行p/v操作 | semid:信号量id sops:信号量操作结构体数组 struct sembuf{short sem_num;(信号量编号)short sem_op;(信号量pv操作) short sem_flg;(信号量行为SEM_UNDO)} nsops:信号量数量 | 成功:0 失败:-1 |
补充:
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) */
};
4、示例
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/sem.h>
static int gSemNum = 0;
union semun
{
/* data */
int val;
struct semid_ds *buf;
};
/*
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
};
*/
/*
*封装函数
*/
int semInit(int semId, int initVal)
{
union semun uSemun;
uSemun.val = initVal;
return semctl(semId, gSemNum, SETVAL, uSemun);
}
int semDelete(int semId)
{
union semun uSemnum;
return semctl(semId, gSemNum, IPC_RMID, uSemnum);
}
int semTake(int semId)
{
struct sembuf sops;
sops.sem_flg = SEM_UNDO;
sops.sem_num = 0;
sops.sem_op = -1;
return semop(semId, &sops, 1);
}
int semGive(int semId)
{
struct sembuf sops;
sops.sem_flg = SEM_UNDO;
sops.sem_num = 0;
sops.sem_op = 1;
return semop(semId, &sops, 1);
}
int main(char *argc, char **argv)
{
key_t key;
int semId;
pid_t pid;
/*获取key*/
key = ftok("./", 1);
if(key == -1)
{
printf("Get key failed!\r\n");
exit(-1);
}
/*创建信号量*/
semId = semget(key, 1, 0666 | IPC_CREAT);
if(semId == -1)
{
printf("Get sem id failed\r\n");
exit(-1);
}
/*初始化信号量*/
if(semInit(semId, 0) == -1)
{
printf("Sem init failed!\r\n");
exit(-1);
}
/*创建子进程*/
pid = fork();
if(pid == -1)
{
printf("Create child process failed!\r\n");
exit(-1);
}
else if(pid == 0)
{
printf("Wait sem give!\r\n");
if(semTake(semId) == -1)
{
printf("Sem take failed!\r\n");
semDelete(semId);
exit(-1);
}
printf("Sem take ok!");
}
else
{
sleep(3);
printf("Give sem!\r\n");
if(semGive(semId) == -1)
{
printf("Sem give failed!\r\n");
semDelete(semId);
exit(-1);
}
}
semDelete(semId);
exit(0);
}
5、现象
Wait sem give!
Give sem!
Sem take ok!
三、system-v 共享内存
1、介绍
共享内存有在不同进程之间进行高效、大量传输数据功能。共享内存是在两个进程之间把同一片的虚拟地址空间映射到同一物理空间上面。在进程写入数据时另外一个进程就能在这片屋里空间督导写入的数据了。
2、共享内存的用法
- 生成一个唯一 key 值
- 创建共享内存(shmget)
- 共享内存映射(shmat)
- 解除共享内存映射(shmdt)
- 删除共享内存(shmctl)
3、常用函数
函数 | 功能 | 参数 | 返回值 | 头文件 |
---|---|---|---|---|
int shmget(key_t key, int size, int shmflg) | 获取共享内存id | key:键值 size:共享内存大小 shmflg: IPC_CREAT:创建共享内存 mode:权限 | 成功:共享内存id 失败:-1 | sys/ipc.h sys/shm.h |
int shmat(int shmid, const void *shmaddr, int shmflg) | 映射共享内存 | shmid:共享内存id shmaddr:映射地址(NULL为自动分配) shmflg: SHM_RDONLY:只读 0:可读可写 | 成功:共享内存首地址 失败:-1 | |
int shmdt(const void *shmaddr); | 解除共享内存映射 | shmaddr:映射地址 | 成功:0 失败:-1 | |
int shmctl(int shmid, int cmd, struct shmid_ds *buf); | 设置共享内存相关属性 | shmid:共享内存id cmd: IPC_STAT:获取共享内存属性信息 IPC_SET:设置共享内存属性 IPC_RMID:删除共享内存 buf:属性缓存 | 成功:由cmd属性决定 失败:-1 |
4、示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(char *argc ,char **argv)
{
pid_t pid;
key_t key;
int shmid;
char *addr;
/*获取键值*/
key = ftok("./", 1);
/*获取共享内存id*/
shmid = shmget(key, 100, IPC_CREAT | 0666);
if(shmid < 0)
{
printf("Get share memory id failed!\r\n");
exit(-1);
}
pid = fork();
if(pid < 0)
{
printf("Create sub process failed\r\n");
exit(-1);
}
else if(pid == 0)
{
printf("Sub process cteated!\r\n");
/*映射共享*/
addr = shmat(shmid, NULL, 0);
if(addr < 0)
{
printf("Remap shm failed!\r\n");
exit(-1);
}
memcpy(addr, "Hello shm!\r\n", strlen("Hello shm!\r\n"));
printf("Sub process exit!\r\n");
sleep(4);
}
else
{
sleep(3);
/*映射共享*/
addr = shmat(shmid, NULL, 0);
if(addr < 0)
{
printf("Remap shm failed!\r\n");
exit(-1);
}
printf("Shm data is:%s", addr);
if(shmdt(addr) < 0)
{
printf("Shmdt failed!\r\n");
exit(-1);
}
if(shmctl(shmid, IPC_RMID, NULL) < 0)
{
printf("Delete shm failed!\r\n");
exit(-1);
}
}
exit(0);
}
5、现象
Sub process cteated!
Sub process exit!
Shm data is:Hello shm!