IPC进程间通信

进程间通信IPC

1>由于多个进程的用户空间是相互独立的,其栈区,堆区,静态区的数据都是彼此私有的,所以不可能通过用户空间中大的区域完成多个进程之间数据的通信

2>可以使用外部文件来完成多个进程之间数据的传递,一个进程向文件中写入数据,另一个进程从文件中读取数据,该方式要必须保证写进程先执行,然后再执行读进程,要保证进程执行的同步性

3>我们可以利用内核空间来完成对数据的通信工作,本质上,在内核空间创建一个特殊的区域,一个进程向该区域中存放数据,另一个进程可以从该区域中读取数据

3.1进程间通信的基础概念

1>IPC:进程间通信

2>使用内核空间来完成多个进程间相互通信,根据使用的容器或方式不同,分为三类通信机制

3>进程间通信方式分类

1.内核提供的通信方式(传统的通信方式)

无名通道

有名通道

信号

2.system V提供的通信方式

消息队列

共享内存

信号量(信号灯集)

   3.套接字通信:socket网络编程(跨主机通信)

管道通信特点:

1.管道可以实现自己给自己发消息

2.对管道中数据的操作是一次性的,当管道中的数据被读取后,就从管道中消失了,再读取时会被阻塞

3管道文件大小:64k

4由于返回的是管道文件的文件描述符,所以对管道的操作只能是IO相关函数,但是,不可以lseek对光标进行偏移,必须做到先进先出

5管道的读写特点:

当读端存在时:写端有多少写多少,直到写满64K后,在write处阻塞

当读端不存在时:写端再向管道中写入数据,会发生管道破裂,内核空间会向空间发射SIGPIPE信号,进程收到信号后,退出

当写端存在时:读端有多少会读多少,没有数据会在read出阻塞

当写端不存在时:读端有多少会读多少,没有数据不会在read出阻塞

管道通信是半双工通信方式

单工:只能进程A向进程B发送消息

半双工:同一时刻只能A向B发送消息

全双工:任意时刻,AB可以互相通信

有名管道:

1>有名管道,会在文件系统中创建一个真实存在的管道文件

2>既可以完成亲缘间通信,也可以完成非亲缘进程间通信

3>有名管道API

#include<sys/types.h>

#include<sys/stat.h>

int mkfifo(const char*pathname,mode_t mode);

功能:创建一个管道文件,并存在于系统文件中

参数1:管道文件名字

参数2:管道文件的权限,内容详见open函数的mode参数

返回值:成功返回0,失败返回-1并置为错误码

注意:管道文件被创建后,其他线程就可以进行打开读写操作了,但是,必须要保证当前管道文件的两端都打开后,才能进行读写操作,否则会在open处阻塞

信号:

1>信号相关概念

1.信号是软件模拟硬件的中断功能,信号是软件实现的,中断是硬件实现的

中断:停止当前正在执行的事情,去做另外一件事

2.信号是Linux内核实现的,没有内核就没有信号的概念

3,用户可以给进程发信号:例如CTRL+C

内核可以向进程发送消息例如:SIGPIPE

一个进程可以给另一个进程发送信号,需要通过相关的函数来实现

4.信号通信是属于异步通信工作

同步:多个任务有先后顺序

异步:多个任务没有先后顺序

对信号的三种处理方式:捕获,忽略,默认

5对信号的处理函数:signal

#include<signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum,sighandler_t handler);
功能:将信号与信号处理方式绑在一起

参数1:要处理的信号

参数2:处理方法:

SIG_IGN :忽视

SIG_DEL 默认,一般默认的信号处理操作都是杀死进程

typedef void (*sighandler_t)(int)用户自定义的函数

返回值:成功返回处理方式的起始地址,失败返回SIG_ERR并置为错误码

注意:只要程序与信号绑定一次,后续但凡程序收到该信号,对应的处理就会立即响应

6使用信号的方式完成对僵尸进程的回收

当子进程退出后,会向父进程发送一个SIGCHLD的信号,当父进程收到该信号后,可以将其进行捕获,在信号处理函数中,可以以非阻塞的方式回收僵尸进程

信号发送信号:

#include<signal.h>

int kill(pid_t pid,int sig);

功能:向指定进程或进程组发送信号

参数1:进程号或进程组号

>0表示向执行进程发送信号

=0向当前进程所在的进程组中的所有进程发送信号

=-1向所有进程发送信号

<-1向指定进程组发送信号,进程组ID号为pid的绝对值

参数2:要发送的信号

返回值:成功返回0,失败返回-1并置为错误码

int raise(int sig);

功能:向自己发送信号 等价于:kill(getpid(),sig);

参数:要发送的信号

返回值:成功返回0,失败返回非0数组

总结:

信号可以完成多个进程间的通知作用,但是不能实行数据的传输

有关system V进程间通信对象相关的指令

ipcs 可以查看所有的信息(消息队列,共享内存,信号量)

ipcs -q可以查看消息队列的信息

ipcs -m可以查看共享内存的信息

ipcs -s:可以查看信息量的信息

ipcrm -q/m/s ID:可以删除指定ID的IPC对象

上述的三种通信方式,也是借助内核空间完成的相关通信,原理是在内核空间创建出相关的对象容器,在进行进程间通信时,可以将信息放入对象中,另一个进程就可以从该容器中取数据了.

与内核提供的管道,信号通信不同:system V的ipc对象实现了数据传递的容器与程序相分离,也就是说,即使程序已经结束,但是,放入到容器中的数据依旧存在,除非将容器手动删除

3.6消息队列

消息队列实现的API

.创建key值

#include<sys/types.h>

#include<sys/ipc.h>

key_t ftok(const char*pathName,int proj_id);

功能:通过给定的文件以及给定的一个随机值,创建出一个4字节整数的key 值,用于system V IPC对象的创建

参数1:一个文件路径,要求是已经存在的文件路径,提供了key值3字节内容,其中,文件的设备号占1字节,文件的inode号占2字节

返回值:成功返回key值,失败返回-1并置为错误码

2通过key值,创建消息队列

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgget(key_t key,int msgflg);

功能:通过key值,创建出一个消息队列的对象,并返回消息队列的句柄ID,后期可以通过该ID操作整个消息队列

参数1:key值,该值可以 是IPC_PRIVATE,也可以是fork创建出来的,前者只用于亲缘进程间的通信

参数2:创建标识:

IPC_CREAT:创建并打开一个消息队列,如果该文件存在则直接打开

IPC_EXCL::确保创建出一个新的消息队列,如果该文件已存在则报错,错误码EXIST

返回值:成功返回消息队列的ID号,返回-1并置为错误码

向消息队列中存放数据

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgsnd(int msqid,const void*msgp,size_t msgsz,int msgflg);

功能:向消息队列中存放一个指定格式的消息

参数1:打开的消息队列的id号

参数2:要发送的消息的起始地址,消息一般定义为一个结构体类型,由用户手动定义

struct msgbuf{

long mtype;/*message type,must be>0*/消息的类型

char mtext[1];/*message data*/消息正文

...

};

参数3:消息正文的大小

参数4:是否阻塞的标志

0:标识阻塞形式向消息队列中存放消息,如果消息队列满了,就在该函数处阻塞

IPC_NOWAIT:标识非阻塞的形式向消息队列中存放消息,如果消息队列满了,直接返回

返回值:成功返回0,失败返回-1并置为 错误码

4.从消息队列中取消息

ssize_t msgrcv(int msqid ,void*msgp,size_t msgsz,long msgtype,int msgflag);

功能:从消息队列中读取指定类型的消息放入给定的容器

参数1:打开的消息队列的id号

参数2:要接受的消息的起始地址,消息一般定义为一个结构体类型,由用户手动定义

struct msgbuf{

long mtype;/*message type,must be>0*/消息的类型

char mtext[1];/*message data*/消息正文

...

};

参数3:消息正文的大小

参数4:要接受的消息类型

0:表示每次都读取队列中的第一个消息,无论类型

>0:读取队列中第一个类型为msgtyp的消息

<0:读取队列中的一个消息,消息为绝对值小于msgtype的第一消息

参数5:是否阻塞的标志

0:标识阻塞形式向消息队列中存放消息,如果消息队列满了,就在该函数处阻塞

IPC_NOWAIT:标识非阻塞的形式向消息队列中存放消息,如果消息队列满了,直接返回

返回值:成功返回实际读取的正文大小,失败返回-1并置为错误码

5.销毁消息队列

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgctl(int msqid,int cmd,struct msqid_ds*buf);

参数1:消息队列的ID号

参数2:要执行的操作

IPC_RMID:删除一个消息队列,当cmd为该值时,第三个参数可以省略填NULL即可

IPC_STAT:表示获取当前消息队列的属性,此时第三个参数就是存放获取消息队列属性的容器起始地址

IPC_SET:设置当前消息队列的属性,此时第三个参数就是要设置消息队列的属性数据的起始地址

参数3:消息队列数据容器结构体,如果第二个参数为IPC_RMID,则该参数可忽略填NULL即可,如果是IPC_STAT,IPC_SET填如下结构体:

成功返回0,失败返回-1并置为错误码

5.注意事项:

1.对于消息而言,由两部分组成:消息的类型和消息正文,消息结构体由用户自定义

2.对于消息队列而言,任意一个进程都可以向消息队列中发送消息,也可以从消息队列中取消息

3.多个进程,使用相同的key值打开的是同一个消息队列

4.对消息队列中的消息读取是一次性的,被读取后,消息队列中不存在该消息了

5.消息队列的大小:16K

共享内存的API

1.创建key值

#include<sys/types.h>

#include<sys/ipc.h>

key_t ftok(const char*pathName,int proj_id);

功能:通过给定的文件以及给定的一个随机值,创建出一个4字节整数的key 值,用于system V IPC对象的创建

参数1:一个文件路径,要求是已经存在的文件路径,提供了key值3字节内容,其中,文件的设备号占1字节,文件的inode号占2字节

返回值:成功返回key值,失败返回-1并置为错误码

2.通过key值创建共享内存段

#include<sys/ipc.h>

#include<sys/shm.h>

int shmget(key_t key,size_t size,int shmflg);

功能:申请指定大小的物理内存,映射到内核空间,创建出共享内存段

参数1:key 值,可以是IPC_PRIVATE,也可以是ftok创建出来的key值

参数2:申请的大小,是一页(4096字节)的整数倍,并且向上取整

参数3:创建标识

IPC_CREAT:创建并打开一个共享内存,如果共享内存存在,则直接打开

IPC_EXCL:确保本次创建处理的是一个新的共享内存,如果共享内存已经存在,则报错,错误码为EEXIST

返回值:成功返回共享内存段的ID,失败返回-1

3

#include<sys/types.h>

#include<sys/shm.h>

void*shmat(int shmid,const void*shmaddr,int shmflg);

功能:将共享内存段映射到用户空间

参数1:共享内存的ID号

参数2:物理内存的起始地址,一般填NULL,由系统自动选择一个合适的对齐页

参数3:对共享内存段的操作
0:表示读写操作

SHM_RDONLY:只读

返回值:成功返回用于操作共享内存的指针,失败返回(void*)-1并置为错误码

4释放内存的映射关系

int shmdt(const void*shmaddr);

功能:将进程与共享内存的映射取消

参数:共享内存的指针

返回值:成功返回0,失败返回-1并置为错误码

 5.共享内存的控制函数

#include<sys/ipc.h>

#include<sys/shm.h>

int shmctl(int shmid,int cmd,struct shmid_ds*buf);

功能:根据给定的不同的cmd执行不同的操作

参数1:共享内存的ID

参数2:要操作的指令

IPC_RMID删除共享内存段,第三个参数可以省略

IPC_STAT 获取当前共享内存的属性

IPC_SET设置当前共享内存的属性

参数3:如果参数2为IPC_RMID,则参数3可以省略填NULL,如果参数2为另外两个

则参数3为以下结构体

成功返回0,失败返回-1,并置为错误码

信号量

信号量:本质上是维护一个value值,是一个整数,有进程申请信号号量资源时,如果该信号量资源大于0,则进行递减操作,如果当前信号量的值为0,则申请资源的进程处于阻塞状态,直到另一个进程的value值增加到大于0当有进程对该信号量进行释放操作时,该value值会递增

1.创建key值

#include<sys/types.h>

#include<sys/ipc.h>

key_t ftok(const char*pathname,int proj_id); //ftok(“/”,’k’);

功能:通过给定的文件以及给定的一个随机值,创建出一个4字节整数的key值,用于 system V IPC对象的创建

参数1:一个文件路径,要求是已经存在的文件路径,提供了key值3字节的内容,其中,文件的设备号占1字节,文件的inode号占2字节

参数2:一个随机整数,取后8位(1字节)跟前面的文件共同组成key值,必须是非0的数字

返回值:成功返回key值,失败返回-1并置为错误码

2.通过key值创建信号量集

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semget(key_t key,int nsems,int semflg);

功能:通过给定的key值创建一个信号集

参数1:key值,该值可以是IPC_PRIVATE,也可以是ftok创建出来的,前者只用于亲缘进程间的通信

参数2:信号量数组中信号量个数

参数3:创建标识

IPC_CREAT:创建并打开一个信号量集,如果信号量集已经存在,则直接打开

IPC_EXCL:确保本次创建处理的是一个新的信号量集,如果信号量集 已经存在,则报错,错误码为EEXIST

0664:该消息队列的操作权限

3.关于信号量集的操作:P(申请资源)V(释放资源)

#include< sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semop(int semid,struct sembuf*spos,size_t nspos);

功能:完成对信号量数组的操作

参数1:信号量数据ID号

参数2:有关信号量操作的结构体变量起始地址,该结构体中包含了操作的信号量编号和申请还是释放的操作

struct sembuf{

unsigned short sem_num;//要操作的信号量的编号

short sem_op;//要进行的操作,大于0表示释放资源,小于0表示申请资源

short sem_flg;//操作标识位,0标识阻塞方式IPC_NOWAIT

}

参数3:本次操作的信号量的个数

返回值:成功返回0,失败返回-1并置为错误码

4.关于信号集的控制函数

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semctl(int semid,int semnum,int cmd);

功能:指向有关信号量集的控制函数,具体控制内容取决于cmd

参数1:信号量集的ID

参数2:要操作的信号量的编号,编号是从0开始

参数3:要执行的操作

IPC_RMID:表示要删除信号量集,cmd为该值时,参数2可以忽略,参数4可以不填

SETVAL:表示对参数2对应的信号量进行设置操作(初始值)

GETVAL:表示对参数2对应的信号量进行获取值操作

SETALL:设置信号量集中所有信号量的值

GETALL:获取信号量集中的所有信号量的值

IPC_STAT:表示获取当前信号量集的属性

IPC_SET:表示设置当前信号量的属性

参数4:根据不同的cmd值,填写不同的参数值,所以该处是一个共用体变量

union semum{

int val

struct semid_ds*buf;

unsigned short*array;

struct seminfo*_buf;

};

返回值:成功时,SETVAL返回0,GETVAL返回当前信号量的值,失败返回-1并置为错误码

例如:

1)给0号信号量设置初始值为1;

union semun us; //定义一个共同体变量

us.val=1; //对该公用体变量赋值

semctl(semid,0,SETVAL,us);//该函数完成了对0号信号量设置初始值为1的操作

2)删除信号量集:

semctl(semid,0,IPC_RMID);

注意:

1.信号量集是完成多个进程间同步问题的,一般不进行信息的通信

2.信号量集的使用本质上是对多个value值进行管控,每个信号量控制一个进行,在进程执行前,申请一个信号量的资源,执行后,释放另一个信号量的资源

3.如果当前进程的信号量值为0,则当前进程在申请处阻塞,直到其他进程将该信号量中的资源增加到大于0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值