进程间通信(💥IPC 对象)
共享内存
直接读写”内核的内存“,它是效率最高的进程间通信方式
- 为了再多个进程间交换信息,内核专门留出一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
- 由系统跳转到内核帮用户映射内核中的内存给用户(注意不是拷贝)
//表示IPC对象的key:
//为了避免生成不一样的key,用同样的pathname和proj_id只会生成一样的
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
//功能:用文件的inode节点号和用户提供的数据组合生成一个key
pathname:文件名(包含路径)
proj_id:用户提供的数据
打开、创建、共享内存对象
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
//创建/打开一个共享内存对象
//返回值:成功返回ID号,失败返回-1
key:要打开或者要创建的对象的密钥
为0或者不写就表示PRIVATE
size:要创建的共享内存大小
shmflg:打开或者创建时的权限
IPC_CREAT:不存在则创建
IPC_EXCL:如果存在就报错
🌴Linux中使用ipcs可以查看 消息队列 共享内存
映射空间地址
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
//功能:把内核中的共享内存空间映射到用户空间
//返回值:成功返回映射后的空间地址,失败返回(void *)-1,表示0XFFFF;
shmid:共享内存的id号
shamddr:可以指定要映射的空间地址
NULL:表示系统决定
0X...:表示要把内核空间映射到这个地址
fhmflg:共享内存的操作权限(读写)
SHM_RDONLY:表示只读(不用)
0:表示可读可写
取消映射
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
//功能:把shmat映射后的空间取消掉
//返回值
控制共享内存对象
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmid_ds结构体
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Creation time/time of last modification via shmctl() */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
//功能:整体控制共享内存对象
//返回值:成功返回0,失败返回-1
shimid:共享内存对象ID号
cmd:要执行的操作
IPC_STAT 获取对象属性
IPC_SET 设置对象属性
IPC_RMID 删除对象
buf:用于设置或者获取对象的属性,如果是删除对象(IPC_RMID),写NULL
//注:删除对象并不是直接删除:而是标记这个对象为删除状态,当有一个进程标记该对象为删除后并且没有其他对象使用时,系统才正式删除该对象
练习_v1
注意:用 sudo 去运行,否则会出现permission denied
//ftok中第一个参数没有使用拥有足够权限的文件,需要加sudo
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main(int argc,char *argv[])
{
key_t shmkey=ftok(".","zx");
printf("shmkey = %d\n",shmkey);
int shmid=shmget(shmkey,100,IPC_CREAT|0777);
if(shmid<0){
perror("shmget");
return -1;
}
printf("shmid = %d\n",shmid);
char *shmadd=shmat(shmid,NULL,0);
if(shmadd == (void *)-1){
perror("shmat");
return -1;
}
fgets(shmadd,64,stdin);
printf("shmadd = %s\n",shmadd);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
练习_v2
//ftok中第一个参数使用带有足够权限的文件,就不需要加sudo
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main(int argc,char *argv[])
{
key_t shmkey=ftok("a.txt",122234);
printf("shmkey = %d\n",shmkey);
int shmid=shmget(shmkey,100,IPC_CREAT|0777);
if(shmid<0){
perror("shmget");
return -1;
printf("shmid = %d\n",shmid);
char *shmadd=shmat(shmid,NULL,0);
if(shmadd == (void *)-1){
perror("shmat");
return -1;
}
fgets(shmadd,64,stdin);
printf("shmadd = %s\n",shmadd);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
关闭共享内存、信号量数组
ipcrm [选项] ipcrm shm|msg|sem …
移除某个 IPC 资源。
选项: -m, --shmem-id 按 id 号移除共享内存段 -M, --shmem-key <键> 按键值移除共享内存段 -q, --queue-id 按 id 号移除消息队列 -Q, --queue-key < 键 > 按键值移除消息队列 -s, --semaphore-id 按 id 号移除信号量 -S, --semaphore-key < 键 > 按键值移除信号量 -a, --all [=<shm|msg|sem>] (将指定类别中的) 全部移除 -v, --verbose 解释正在进行的操作
-h, --help display this help -V, --version display version
信号灯集合
二值信号灯:值为 0 或者 1。
创建、打开信号灯集对象
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
//功能:创建或者打开一个信号灯集对象
//返回值:成功返回ID号,失败返回-1
key:要打开或者创建对象时的“密钥”
nesms:指明要创建的信号量集包含的信号量个数。如果只是打开信号量,把nsems设置为0即可。该参数只在创建信号量集时有效。
semflg为操作标识,可取如下值:
0:取信号量集标识符,若不存在则函数会报错
IPC_CREAT:当semflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的信号量集,则新建一个信号量集;如果存在这样的信号量集,返回此信号量集的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的信号量集,则新建一个消息队列;如果存在这样的信号量集则报错
上述semflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限
信号灯集的操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
//结构体:
struct sembuf
{
unsigned short int sem_num; /* semaphore number */
short int sem_op; /* semaphore operation */
short int sem_flg; /* operation flag */
};
//功能:申请或者释放资源
//返回值:成功返回0,失败返回-1
semid:信号灯集ID
信号灯集的整体控制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, .../*union semun arg*/);
//功能:整体返回控制信号
//返回值:成功返回0,失败返回-1
semid:要操作的id号
semnum:要修改的信号灯编号(type)
cmd:要执行的指令
GETVAL:获取信号灯的值
SETVAL:设置信号灯的值
IPC_RMID:从系统中删除信号灯集合
//第四个参数的共用体
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) */
};
//如果需要设置值,那么会有第四个参数(共用体),这个共用体里的val成员表示给信号灯初始化的个数
//🔑注:如果是删除,不需要共用体。只需要再semnum这个参数随便写一个信号灯集的编号就能整体删完对象
练习
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main(int argc,char *argv[])
{
key_t key=ftok("a.txt",111);
if(key<0){
perror("ftok");
return -1;
}
printf("key= %d \n",key);
/*2. create se,*/
int semid =semget(key,3,IPC_CREAT | 0666);
//3表示有3种信号量
if(semid<0){
perror("semget");
return -1;
}
printf("sem id = %d\n",semid);
return 0;
}
结果如图

消息队列
例子
- 两个终端发消息,类似qq
//client_a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
typedef struct msgbuf{
long mtype;
char mtext[512];
}MSG;
#define LEN (sizeof(MSG)-sizeof(long))
//send massage
void send_msg(int msgid,char *buf){
int ret=-1;
if(( ret=msgsnd(msgid,buf,LEN,0))<0){
perror("msgsnd");
}
}
void* receive_msg(){
key_t key=ftok("aa.txt",'a');
MSG msgbuf;
memset(msgbuf.mtext,-1,512);
int qid=msgget(key,IPC_CREAT|0664);
while(1){
int ret=msgrcv(qid,&msgbuf,LEN,1,0);
if(ret<0){
perror("msgrcv");
}else{
printf("%s\n",msgbuf.mtext);
}
}
}
int main(){
MSG msgbuf;
msgbuf.mtype=2;
memset(msgbuf.mtext,-1,512);
key_t key=ftok("aa.txt",'a');
if(key<0){
perror("ftok");
return -1;
}
pthread_t pt;
pthread_create(&pt,NULL,receive_msg,NULL);
int msgid=-1;
if(msgid=msgget(key,IPC_CREAT|0664)<0){
perror("msgget");
return -1;
}
while(1){
fgets(msgbuf.mtext,LEN,stdin);
send_msg(msgid,&msgbuf);
}
return 0;
}
//client_b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
typedef struct msgbuf{
long mtype;
char mtext[512];
}MSG;
#define LEN (sizeof(MSG)-sizeof(long))
//send massage
void send_msg(int msgid,char *buf){
int ret=-1;
if(( ret=msgsnd(msgid,buf,LEN,0))<0){
perror("msgsnd");
}
}
void receive_msg(){
key_t key=ftok("aa.txt",'a');
MSG msgbuf;
memset(msgbuf.mtext,-1,512);
int qid=msgget(key,IPC_CREAT|0664);
while(1){
int ret=msgrcv(qid,&msgbuf,LEN,2,0);
if(ret<0){
perror("msgrcv");
}else{
printf("%s\n",msgbuf.mtext);
}
}
}
int main(){
MSG msgbuf;
msgbuf.mtype=1;
memset(msgbuf.mtext,-1,512);
key_t key=ftok("aa.txt",'a');
if(key<0){
perror("ftok");
return -1;
}
pthread_t pt;
pthread_create(&pt,NULL,receive_msg,NULL);
int msgid=-1;
if(msgid=msgget(key,IPC_CREAT|0664)<0){
perror("msgget");
return -1;
}
while(1){
fgets(msgbuf.mtext,LEN,stdin);
send_msg(msgid,&msgbuf);
}
return 0;
}