1.IPC(进程通信)对象
进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信机制。
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
IPC对象:内存文件
ipcs 查看所有ipc对象的信息
ipcrm -Q/M/S Key 删除ipc对象的消息队列/共享内存/信号灯
ipcrm -q/m/s 消息队列ID/共享内存ID/信号灯ID
ftok
函数是一个用于生成合法键值(KEY)的函数,通常用于创建 IPC 对象,如消息队列、共享内存和信号量的键值。它通过将文件路径名和项目ID结合起来生成一个唯一的键值。
key_t ftok(const char *pathname, int proj_id); 功能: 创建一个IPC对象名称 参数: pathname:文件的路径(路径不同,生成的对象也不同) proj_id:项目ID(8bits) 返回值: 成功返回IPC对象名称 失败返回-1 |
2.消息队列
每个消息都有一个类型和内容,使得进程可以根据类型选择性地接收消息,消息队列提供一种带有数据标识的特殊管道。
1.创建IPC对象名称 fork()
2.创建消息队列 msgget
int msgget(key_t key, int msgflg); 功能: 创建一个消息队列 参数: key:IPC对象名称 msgflg:消息队列属性 IPC_CREAT:创建一个消息队列 IPC_EXCL: 如果消息队列存在就报错 返回值: 成功返回消息队列ID 失败返回 -1 |
3.发送消息msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 功能: 向消息队列中发送消息 参数: msqid:消息队列的ID号 msgp:发送消息的内容 msgsz:发送消息的大小 msgflg:消息属性 默认为0 返回值: 成功返回0 失败返回-1 |
4.接收消息msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 功能: 从消息队列中接收消息 参数: msqid:消息队列的ID号 msgp:存放消息的空间首地址 msgsz:最多接收消息的大小 msgtyp:接收消息的类型 msgflg:消息属性 默认为0 返回值: 成功返回接收到数据的字节数 失败返回-1 |
5.消息队列销毁.msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf); 功能: 向消息队列发送命令 参数: msqid:消息队列的ID号 cmd:命令 IPC_STAT:获取消息队列的信息 返回值: 成功返回0 失败返回-1 |
练习:
#include "../head.h"
int main(void)
{
key_t key;
int msgid;
int ret = 0;
message_t sendmsg;
//1.创建IPC对象 Key 值
key = ftok("/", 'b');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建消息队列
msgid = msgget(key, IPC_CREAT | 0664);
if (-1 == msgid)
{
perror("fail to shmget");
return -1;
}
sendmsg.mtype = 100;
fgets(sendmsg.mtext, sizeof(sendmsg.mtext), stdin);
sendmsg.mtext[strlen(sendmsg.mtext)-1] = '\0';
//3.发送消息
ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg.mtext), 0);
if (-1 == ret)
{
perror("fail to msgsnd");
return -1;
}
return 0;
}
#include "../head.h"
int main(void)
{
key_t key;
int msgid;
ssize_t nsize = 0;
message_t recvmsg;
//1.创建IPC对象 Key 值
key = ftok("/", 'b');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建消息队列
msgid = msgget(key, IPC_CREAT | 0664);
if (-1 == msgid)
{
perror("fail to shmget");
return -1;
}
//3.接收消息
nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
if (-1 == nsize)
{
perror("fail to msgrcv");
return -1;
}
//4.打印消息
printf("RECV:%s\n", recvmsg.mtext);
//5.删除消息队列
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
3.共享内存
共享内存是效率最高的 IPC
1.创建 key 值 fork()
2.创建共享内存 shmget
int shmget(key_t key, size_t size, int shmflg); 功能: 创建一个共享内存 参数: key:IPC对象名称 size:共享内存的大小(PAGE_SIZE(4k) 的整数倍) shmflg: IPC_CREAT 创建 IPC_EXCL 如果存在就报错 返回值: 成功返回共享内存ID号 失败返回-1 |
3.映射共享内存 shmat
void *shmat(int shmid, const void *shmaddr, int shmflg); 功能: 将地址映射到共享内存中 参数: shmid:共享内存ID号 shmaddr: NULL: 让系统选择一个合适的地址映射到共享内存中 shmflg: 属性,默认为0 返回值: 成功返回映射到共享空间的地址 失败返回NULL |
4.解除映射 shmdt
int shmdt(const void *shmaddr); 功能: 解除映射空间 参数: shmaddr:映射到共享内存中的地址 返回值: 成功返回0 失败返回-1 |
5.销毁共享内存 shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf); 功能: 向共享内存发送命令 参数: shmid:共享内存ID号 cmd:命令 IPC_RMID 删除 返回值: 成功返回0 失败返回-1 |
练习:
#include "../head.h"
int main(void)
{
int ret = 0;
int shmid = 0;
char *pshmaddr = NULL;
//1.创建 key 值
key_t key;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.映射共享内存空间
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
//4.从终端接收写入到共享内存中
while (1)
{
gets(pshmaddr);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
}
//5.解除映射
ret = shmdt(pshmaddr);
if (-1 == ret)
{
perror("fail to shmdt");
return -1;
}
return 0;
}
#include "../head.h"
int main(void)
{
int ret = 0;
int shmid = 0;
char *pshmaddr = NULL;
//1.创建 key 值
key_t key;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.映射共享内存空间
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
while (1)
{
printf("内容:%s\n", pshmaddr);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
}
//4.解除映射
ret = shmdt(pshmaddr);
if (-1 == ret)
{
perror("fail to shmdt");
return -1;
}
//5.删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
4.信号灯(有名信号量数组)
它可以实现进程之间的同步与互斥,可以实现共享内存的同步
它通过管理一个计数器来控制对资源的访问
1.创建 key 值 fork()
2.创建信号量数组 semget
int semget(key_t key, int nsems, int semflg); 功能: 创建信号量数组 参数: key:IPC对象名称 nsems:信号量个数 semflg:信号量属性 IPC_CREAT:创建一个信号量数组 返回值: 成功返回0 失败返回-1 |
3.向信号灯发送命令 semctl(SETVAL可以完成初始化)
int semctl(int semid, int semnum, int cmd, ...); semid 信号量 ID cmd: 设置该信号量元素的值(资源个数) |
union semun
{
int val; /* 当 cmd 为 SETVAL 时使用 */
struct semid_ds *buf; /* 当 cmd 为 IPC_STAT 或 IPC_SET 时使用 */
unsigned short *array; /* 当 cmd 为 GETALL 或 SETALL 时使用 */
struct seminfo *__buf; /* 当 cmd 为 IPC_INFO 时使用 */
};
4.对信号量完成申请和释放操作 semop
int semop(int semid, struct sembuf *sops, size_t nsops); 功能: 对信号量完成申请和释放操作 参数: semid:信号灯ID号 sops:信号灯操作数组 unsigned short sem_num; //操作信号量的下标 short sem_op; //对信号量的操作 +1(释放信号量) -1(申请信号量) short sem_flg; //SEM_UNDO 操作结束后,信号量的值会恢复到原来的值 nsops:数组元素个数 返回值: 成功返回0 失败返回-1 |
#include "../head.h"
/* 对信号量初始化 */
int init_sem(int semid, int *parray, int len)
{
int i = 0;
union semun myun;
int ret = 0;
for (i = 0; i < len; i++)
{
myun.val = parray[i];
ret = semctl(semid, i, SETVAL, myun);
if (-1 == ret)
{
return -1;
}
}
return 0;
}
/* 信号量申请操作 */
int sem_p(int semid, int semnum)
{
int ret = 0;
struct sembuf mybuf;
mybuf.sem_num = semnum;
mybuf.sem_op = -1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
return -1;
}
return 0;
}
/* 信号量释放操作 */
int sem_v(int semid, int semnum)
{
int ret = 0;
struct sembuf mybuf;
mybuf.sem_num = semnum;
mybuf.sem_op = +1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
return -1;
}
return 0;
}
#include "../head.h"
int main(void)
{
//1.创建 key 值
key_t key;
int semid = 0;
int shmid = 0;
int val[2] = {0, 1};
char *pshmaddr = NULL;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.创建信号量数组
semid = semget(key, 2, IPC_CREAT | 0664);
if (-1 == semid)
{
perror("fail to semget");
return -1;
}
//4.对信号量数组初始化
init_sem(semid, val, 2);
//5.映射共享内存
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
while (1)
{
//6.申请读资源
sem_p(semid, 0);
//7.读操作
printf("内容:%s\n", pshmaddr);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
//8.释放写资源
sem_v(semid, 1);
}
shmdt(pshmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
#include "../head.h"
int main(void)
{
//1.创建 key 值
key_t key;
int semid = 0;
int shmid = 0;
int val[2] = {0, 1};
char *pshmaddr = NULL;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.创建信号量数组
semid = semget(key, 2, IPC_CREAT | 0664);
if (-1 == semid)
{
perror("fail to semget");
return -1;
}
//4.对信号量数组初始化
init_sem(semid, val, 2);
//5.映射共享内存
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
while (1)
{
//6.申请写资源
sem_p(semid, 1);
//7.写操作
gets(pshmaddr);
//8.释放读资源
sem_v(semid, 0);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
}
shmdt(pshmaddr);
return 0;
}