进程间的通信技术
进程间的通信就是指不同进程之间的信息交互/交换;
a.文件
b.信号
c.管道
d.共享内存
e.消息队列
f.信号量集
g.网络
其中,d、e、f 三种通信方式统称为XSI IPC通信方式
使用管道实现进程间的通信
管道本质上就是文件,只是该文件比较特殊,叫做管道文件
管道主要分为两大类:
有名管道:主要用于任意两个进程间的通信
无名管道:主要用于父子间进程的通信
有名管道实现进程间的通信
mkfifo a.pipe —— 终端下创建管道文件
echo hello > a.pipe —— 表示写入字符串hello到文件a.pipe,写入阻塞
另起一终端,使用命令cat a.pipe读取管道文件内容,此时可以看到hello,写入端阻塞解除
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参 1:路径名
参 2:权限
功能:创建有名管道文件
使用无名管道实现进程间通信
#include <unistd.h>
int pipe(int pipefd[2]);
功能:主要用于创建单向的数据通道实现进程间的通信,通过参数返回两个文件描述符,
其中pipefd[0]关联管道的读端,pipefd[1]关联管道的写端
注意:管道的特殊性就是在于仅仅作为进程间通信的媒介,而本身并不会存储任何的数据;
管道是一中半双工的通信方式,也就是古老的通信方式
使用共享内存实现进程间的通信
共享内存本质上就是由系统内核维护的一块内存区域,该内存区域可以被映射到两个不同进程的虚拟地址空间中,这两个进程通过都写该内存空间从而实现通信;
共享内存是最快的通信方式;
通信模型
1、获取key值,使用ftok函数
2、创建/获取共享内存,使用shmget函数
3、挂接共享内存,使用shmat函数
4、访问共享内存
5、脱节共享内存,使用shmdt函数
6、如果不再使用, 则删除共享内存, shmctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
key_t key = ftok(const char*pathname, int proj_id);
参 1:字符串形式的路径名,必须关联一个存在的,并且可以访问的文件
参 2:项目的编号,必须是非零低八位二进制位
返回:成功返回生成key值,失败 —— -1
功能:主要用于根据参数指定的路径和项目编号来生成key值
注意:使用相同的路径名和相同的项目编号,生成的key值也相同
int shmid = shmget(key_t key, size_t size,int shmflg);
参 1:key值,ftok函数的返回值
参 2:共享内存的大小
0 - 获取已经存在的共享内存
参 3:具体的操作标志 ( IPC_CREAT | IPC_EXCL | 0664 )
IPE_CREAT —— 不存在则创建,存在则打开
IPE_EXCL —— 与IPC_CREAT搭配使用,存在则创建失败
0 —— 获取已经存在的共享内存
返回:成功返回共享内存ID,失败返回-1
功能:主要用于创建/获取共享内存
void * pv = shmat(int shmid, const void *shmaddr, int shmflg);
参 1:共享内存的ID, shmget函数的返回值
参 2:共享内存的地址,用系统选择地址给NULL
参 3:共享内存的操作标志,默认给0
返回:成功返回挂接地址,失败返回if( (void *)-1 == pv )
功能:主要用于将参数shmid指向的共享内存段挂接到当前正在调用进程的地址空间中
int shmdt(const void *shmaddr);
功能:主要用于脱接参数指定的共享内存从当前的地址空间中,参数一般传shmat函数返回值
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参 1:共享内存的ID, shmaget函数的返回值
参 2:具体的操作命令 IPC_RMID
参 3:结构体指针类型 NULL
功能:主要用于对指针的共享内存执行指定的操作;
常用的基本命令
ipcs -m 表示察看系统中已经存在的共享内存
ipcrm -m 内存id号 共享内存的ID表示删除指定的共享内存
1 //使用共享内存使用进程间的通信
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/ipc.h>
7 #include <sys/shm.h>
8 #include <signal.h>
9
10 //定义全局变了定义共享内存的ID
11 int shmid;
12
13 void fa(int singnum)
14 {
15 printf("正在删除共享内存,请稍后..\n");
16
17 sleep(3);
18
19 //调用shmctl函数实现删除操作
20
21 int res = shmctl(shmid, IPC_RMID, NULL);
22 if(-1 == res )
23 {
24 perror("shmctl");
25 exit(-1);
26 }
27
28 printf("删除共享内存成功\n");
29
30 exit(0); //终止进程
31
32 }
33
34 int main(void)
35 {
36
37 //1.获取key值,使用ftok函数
38
39 key_t key = ftok(".", 100);
40 if (-1 == key )
41 {
42 perror("key error");
43 exit(-1);
44 }
45
46 printf("key = %#x\n", key);
47
48 //2.创建共享内存,使用shmget函数
49
50 shmid = shmget(key, sizeof(int), IPC_CREAT | IPC_EXCL | 0664);
51 if( -1 == shmid )
52 {
53 perror("SHMGET ERROR");
54 exit(-1);
55 }
56
57 printf("shmid = %d\n", shmid);
58
59 //3.挂接共享内存,使用shmat函数
60 void* pv = shmat(shmid, NULL, 0);
61 if( (void *) -1== pv)
62 {
63 perror("shmat");
64 exit(-1);
65 }
66
67 printf("挂接共享内存成功\n");
68 //4.访问共享内存,存放数据100
69
70 *(int *)pv = 100;
71
72 //5脱接共享内存,使用shmdt函数
73
74 int res = shmdt( pv );
75 if(-1 == res )
76 {
77 perror("shmdt");
78 exit(-1);
79 }
80
81 printf("脱接共享内存成功\n");
82 //6.如果不再使用,删除共享内存,shmctl
83
84 printf("删除共享内存按,ctrl+c...\n");
85
86 if( SIG_ERR == signal(SIGINT, fa) )
87 {
88 perror("signal");
89 exit(-1);
90 }
91
92 while(1);
93
94 return 0;
95 }
使用消息队列实现进程间的通信( 重点 )
首先将传递的数据打包成消息,其中一个进程负责将消息发送到消息队列中,另外一个进程负责接受消息队列中的消息,从而实现通信
通信模型
1、获取key值,使用ftok函数
2、创建/获取消息队列,使用msgget函数
3、发送/接收消息,使用msgsnd/msgrcv函数
4、如果不再使用,则删除消息队列,使用msgctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
key_t key = ftok(const char *pathname, int proj_id);
参 1:字符串形式的路径名,必须关联一个存在的,并且可以访问的文件
参 2:项目的编号,必须是非零低八位二进制位
返回:成功返回生成key值,失败 —— -1
功能:主要用于根据参数指定的路径和项目编号来生成key值
注意:使用相同的路径名和相同的项目编号,生成的key值也相同
int msqid = msgget(key_t key, int msgflg);
参 1:key值,ftok的函数返回值
参 2:具体的操作标志 ( IPC_CREAT | IPC_EXCL | 0664 )
IPE_CREAT —— 不存在则创建,存在则打开
IPE_EXCL —— 与IPC_CREAT搭配使用,存在则创建失败
0 —— 获取已经存在的消息队列
返回:成功返回消息队列的ID,失败返回-1
功能:主要用于创建/获取消息队列,从而获得ID
(发送) int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
参 1:消息队列的ID,msgget函数的返回值
参 2:消息的首地址
struct msgbuf {
long mtype; /* 消息的类型,必须 > 0*/
char mtext[1]; /* 消息的数据内容 可以是其它类型 */
};
参 3:用于指定消息内存的大小也就是(char mtext[1])的大小,不包括long mtype
参 4:发送消息的标志 默认0 —— 阻塞状态
功能:主要用于指定消息队列中发送指定消息
(接收) ssize_t msgrcv(int msqid, void *msgp,size_t msgsz, long msgtyp, int msgflg);
参 1:消息队列的ID,msgget函数的返回值
参 2:消息的首地址
struct msgbuf {
long mtype; /* 消息的类型,必须 > 0*/
char mtext[1]; /* 消息的数据内容 可以是其它类型 */
};
参 3:用于指定消息内存的大小也就是(char mtext[1])的大小,不包括long mtype
参 4:消息的类型
0 —— 表示读取消息队列中第一个消息
>0 —— 表示读取消息队列中第一个类型为msgtyp的消息
<0 —— 表示读取消息队列中第一个类型<=msgtypede的绝对值的消息,其中最小的类型优先读取
参 5:发送消息的标志 默认0 —— 阻塞状态
返回:成功返回实际接收数据的大小,失败返回-1
功能:主要用于指定消息队列中接收指定消息
2 1 3 1 2 3 2
-------------------------------
<-- a b c d e f g <----
-------------------------------
当读取的消息类型是0时: a b c e f g
当读取的消息类型是2时: a c g
当读取的消息类型是-2时:b d a c g
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参 1:消息队列的ID,msgget函数返回值
参 2:具体的操作命令IPC_RMID 删除消息队列,此时第三个参数给NULL
参 3:结构体指针
功能:主要用于操作指定消息队列中的消息
常用的基本命令
ipcs -q 察看当前系统已经存在的消息队列
ipcsrm -q 消息队列的ID 表示删除指定的消息队列
使用信号量集实现进程间的通信
信号量本质就是一种计数器,用于控制同时访问同一种共享资源的进程/线程的个数
信号量的工作原理
a、初始化信号量到最大值
b、如果有进程申请到了一个资源,则信号量减一
c、当信号量的数值为0时,让申请资源的进程进入阻塞状态
d、如果有进程释放了一个资源,则信号量加一
e、只要信号量的数值>0,则阻塞的进程就可以继续抢占资源,抢占不到的进程继续阻塞
什么是信号量集
信号量集本质上就是信号量的集合,用于控制多种共享资源被同时访问的进程/线程个数
通信模型
a、获取key值,使用ftok函数
b、创建/获取信号量集,使用semget函数
c、初始化/操作信号量集,使用semctl/semop函数
d、如果不再使用,则删除信号量集,使用semctl函数
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
key_t key = ftok(const char *pathname, int proj_id);
参 1:字符串形式的路径名,必须关联一个存在的,并且可以访问的文件
参 2:项目的编号,必须是非零低八位二进制位
返回:成功返回生成key值,失败 —— -1
功能:主要用于根据参数指定的路径和项目编号来生成key值
注意:使用相同的路径名和相同的项目编号,生成的key值也相同
int semid = semget(key_t key, int nsems, int semflg);
参 1:key值
参 2:信号量集的大小/信号量的个数
0 —— 表示获取已经存在的信号量集
参 3:具体的操作标志 ( IPC_CREAT | IPC_EXCL | 0664 )
0 —— 表示获取已经存在的信号量集
返回:成功返回信号量集的编号,失败返回-1
功能:主要创建/获取信号量集
int semctl(int semid, int semnum, int cmd, ...);
参 1:信号量集的id,semget返回值
参 2:信号量集的下标,从零开始
参 3:具体的操作命令
SETVAL —— 使用第四个参数的数值给信号量集中下标为semnum的信号量进行(初始化)
IPC_RMID —— 删除信号量集,此时忽略第二个参数,不需要第四个参数 (删除)
参 4:可变长参数,是否需要取决于cmd
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
};
功能:主要用于操作指定的信号量集/信号量
int semop(int semid, structsembuf *sops, unsigned nsops);
参 1:信号量集的id,semget返回值
参 2:结构体指针,可以指向结构体变量/数组地址
struct sembuf{
unsigned shortsem_num; /* 信号量集的下标 */
short sem_op; /* 信号量的操作,整数表示增加,负数表示减少,0不变 */
short sem_flg; /* 操作的标志,默认给0 */
}
参 3:结构体指针所指向的结构体数量
功能:主要用于操作信号量集/信号量
ipcs -s —— 察看信号量集
ipcrm -s —— 删除信号量集
ipcs -a —— 察看所有的IPC结构
1 //使用信号量集实现进程间的通信
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/ipc.h>
7 #include <sys/sem.h>
8 #include <signal.h>
9
10 int semid;
11
12 void fa(int signum)
13 {
14
15 printf("正在删除信号量集...\n");
16
17 int res = semctl( semid, 0, IPC_RMID);
18
19 if( -1 == res )
20 {
21 perror( "semctl error" );
22 exit(-1);
23 }
24
25 sleep(3);
26
27 printf("删除成功!");
28
29 exit(1);
30 }
31
32 int main(void)
33 {
34
35 //1.获取key,ftok函数
36
37 key_t key = ftok(".", 200);
38 if(-1 == key )
39 {
40 perror("FTOK ERROR");
41 exit(-1);
42 }
43
44 printf("key = %#x\n", key);
45 //2.创建信号量集,使用semget函数
46 semid = semget(key, 1/*信号量集的大小*/, IPC_CREAT | IPC_EXCL | 0666);
47 if( -1 == semid )
48 {
49 perror("SEMGET ERROR");
50 exit(-1);
51 }
52
53 printf("semid = %d \n", semid);
54 //3.初始化信号量集,使用semctl函数
55 //int arr[1];
56 //int arr[0] = 5;
57 int res = semctl(semid, 0/*信号量下标*/,SETVAL, 5/*信号量的初始值*/);
58 if(-1== res)
59 {
60 perror("semctle");
61 exit(-1);
62 }
63 printf("初始化信号量集成功\n");
64 //4.如果不再使用,删除信号量集,semctl
65
66 printf("请按ctrl+c删除信号量集\n");
67 if( SIG_ERR == signal(SIGINT, fa) )
68 {
69 perror("SIGNAL ERROR");
70 exit(-1);
71 }
72
73 while(1);
74
75 return 0;
76 }
42
43 struct sembuf buf;
44 buf.sem_num = 0;
45 buf.sem_op = -1;
46 buf.sem_flg = 0;
47
48 int res = semop(semid, &buf, 1);
49 if( -1 == res )
50 {
51 perror("semop");
52 exit(-1);
53 }
54
55 printf("进程%d抢占共享资源成功\n", getpid() );
56
57 sleep(20);
58
59 buf.sem_op = 1;
60
61 res = semop(semid, &buf, 1);
62 if(-1 == res )
63 {
64 perror("semop");
65 exit(-1);
66 }
67
68 printf("进程%d释放资源\n", getpid());