6、进程间通信

来源:互联网 发布:微淘数据准确么 编辑:程序博客网 时间:2024/06/05 23:08

Linux进程间通信(IPC:interprocess communication)

Linux进程间通讯的主要方式有:

1、无名管道(pipe)
2、有名管道(FIFO)
3、信号量(semaphore)
4、信号(single)
5、共享内存
6、消息队列
7、套接字(socket)


1、无名管道(pipe)

一个进程在管道的尾部写入数据,另一个进程从管道的头部读出数据。管道包括无名管道和有名管道两种,前者只能用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。

特点:
1. 管道通讯是单向的,有固定的读端和写端。
2. 数据被进程从管道读出后,在管道中该数据就不存在了。

3. 当进程去读取空管道的时候,进程会阻塞。
4. 当进程往满管道写入数据时,进程会阻塞。
5. 管道容量为64KB(#define PIPE_BUFFERS 16   include/linux/pipe_fs_i.h)


在Linux系统中,无名管道一旦创建完成后,操作无名管道等同于操作文件。无名管道的读端被视作一个文件;无名管道的写端也被视作一个文件。

1.创建管道

函数原型: int pipe(int pipefd[2])
函数功能: 创建一个管道
头文件: unistd.h
返回值: 成功返回0,失败返回-1
参数: 参数返回2个文件操作符,pipefd[0]指向管道读端,pipefd[1]指向写端
注意: 执行写之前一定要关闭读文件,同理,读文件也是一样的

2.写管道

函数原型: ssize_t   write(int filedes, const void *buf, size_t  nbytes)
函数功能: 往管道里写数据,类似于文件的写
头文件: unistd.h
返回值: 成功写入字节数的个数
参数: filedes:创建管道后获得的文件描述符,buf:写入数据的缓存,nbytes:写入数据的字节数

3.读管道

函数原型: ssize_t   read(int filedes, void *buf, size_t  nbytes)
函数功能: 读管道里的数据,类似于文件的读
头文件: unistd.h
返回值: 成功读取字节数的个数
参数: filedes:创建管道后获得的文件描述符,buf:存放数据的缓存,nbytes:需要读取数据的字节数

4.关闭管道

函数原型: int  close(int filedes)
函数功能: 关闭一个管道
头文件: unistd.h
返回值: 成功返回0,失败返回-1
参数: filedes:需要关闭的文件描述符

2、有名管道通信

有名管道又称为FIFO文件,因此我们对有名管道的操作可以采用操作文件的方法,如使用open,read,write等.
FIFO文件在使用上和普通文件有相似之处,但是也有不同之处:
1. 读取Fifo文件的进程只能以”RDONLY”方式打开fifo文件。
2. 写Fifo文件的进程只能以”WRONLY”方式打开fifo文件
3.Fifo文件里面的内容被读取后,就消失了。但是普通文件里面的内容读取后还存在。

1.创建有名管道

函数原型: int mkfifo(const char* pathname, mode_t mode)
函数功能: 创建有名管道
头文件: sys/types.h     sys/stat.h
返回值: 成功返回0,失败返回-1
参数: pathname:要创建FIFO文件的名称及路径   mode:文件的访问权限
访问权限与打开文件中的权限类似,mode_t有如下几个数值,如果需要多个模式可以用或运算,这个运算在<fcntl.h>里
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开

O_APPEND 每次写从文件尾追加
O_CREAT 此文件不存在则创建它
O_EXCL 如果指定了O_CREAT。而文件已存在会出错,用此可以测试文件是否存在,不存在则创建
O_TRUNC 若文件存在且为只写或读写打开,则将文件程度截为0
等等,还有其他见《UNIX环境高级编程_第二版》

2.读写管道

读写和文件读写一样,即和无名管道一样

3.打开关闭管道

打开open,关闭close,和文件打开关闭一样

4.删除管道

函数原型: int unlink(const char* pathname)
函数功能: 删除文件(包括FIFO文件)
头文件: unistd.h
返回值: 成功返回0,失败返回-1
参数: 删除文件的名字(包含路径)

3、信号量(semaphore)

信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源(进程互斥)。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。
信号量分为二值信号量,即信号灯的值只能为0或1,可以用来实现互斥,还有计数信号灯,它的值可以为任意非负整数,用来实现同步

1.创建/打开信号量

函数原型: int semget(key_t key, int nsems, int semflag)
函数功能: 获取信号量集合的标识符当key所指定的信号量不一样时,并且semflag参数里包含了IPC_CREAT时,就会创建一个信号量集合,个数由nsems确定
头文件: <sys/types.h><sys/ipc.h><sys/sem.h>
返回值: 如果成功返回信号量集合的标识符,失败返回 -1
参数:
key,它是用来表示不同信号量的数字,类似于身份证号不能重复,所以需要用ftok构造一个key来防止可以冲突
nsems:信号量集合的个数
semflag,标志,可以取IPC_CREAT,

2.信号量操作函数

函数原型: int semop(int semid, struct sembuf *sops, unsigned nsops)
函数功能: 操作信号量集合里的信号量
头文件: <sys/types.h>  <sys/ipc.h>  <sys/sem.h>
返回值: 成功返回0,失败返回-1
参数:
semid:要操作信号量集合的标识符
sops:sembuf结构体包含如下几个成员
sem_num:信号量的编号,从0开始,比如只创建了一个,则编号为0
sem_op:信号量操作的类型   正数释放信号量,负数请求信号量
sem_flg:标志位
nsops:要操作多少个信号量  

3.初始化信号量初始值或者是获取当前信号量的值

函数原型: int semctl(int  semid, int semnum, int cmd)
函数功能: 初始化信号量初始值或者是获取当前信号量的值
头文件: <sys/types.h>  <sys/ipc.h>  <sys/sem.h>
返回值: 成功返回0,失败返回-1
参数:
semid:要操作信号量集合的标识符
semnum:信号量集合中的一个成员
cmd:对信号量的操作


 4、信号(single)

一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

1.发送信号函数

函数原型: int kill(pid_t pid, int sig)
函数功能: 向一个进程发送信号
头文件: <sys/types.h> <signal.h>
返回值: 成功返回0,失败返回-1
参数:
pid    如果pid大于0,pid指向接收信号的进程,
          如果pid等于0,pid指向每一个调用这个进程的进程
 如果pid等于-1
sig 要发送的信号

2.信号处理函数

函数原型: typedef   void (*sighandler_t)(int)             sighandler_t signal(int signum, sighandler_t handler)
函数功能: 设置信号的处理函数或者说处理方式
头文件: <sys/types.h> <signal.h>
返回值: 成功时返回处理函数的指针,失败返回SIG_ERR
参数:
signum  用来指明要处理的信号 kil -l 查看所有参数
handler 对应信号的处理方式 ,可以取值:SIG_IGN忽略这个信号,SIG_DFL交给内核处理, 用户自定义的函数

5、共享内存

共享内存是IPC机制中的一种. 顾名思义,它允许两个不相关的进程访问同一段内存,这是传递数据的一种非常有效的方式。
1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

实例操作:
现在A进程和B进程提供共享内存通信

A:
1、创建共享内存
2、映射共享内存
3、写入通信数据
4、分离共享内存
B:
1、获取共享内存
2、映射共享内存
3、读取数据
4、分离共享内存
5、删除共享内存

1.创建共享内存

函数原型: int shmget(key_t key, size_t size, int flag)
函数功能: 创建或者打开共享内存,并返回其描述符
头文件: <sys/ipc.h>  <sys/shm.h>
返回值: 成功时返回共享内存的描述符,失败时返回-1
参数:
key_t key 键值,信号量的身份证号,如果找不到这个键值,且在标志位中有IPC_CREAT,那么创建一个新的共享内存
size_t size 共享内存的大小 
int flag 打开的标志,使用IPC_CREAT则会创建一个共享内存

2.映射共享内存

函数原型: void *shmat(int shmid, const void *addr, int flag)
函数功能: 把shmid指定的共享内存映射到进程的地址空间里
头文件: <sys/ipc.h>  <sys/shm.h> <sys/types.h>
返回值: 成功时返回映射到进程空间中的内存地址,失败时返回-1
参数:
shmid 要映射的共享内存的描述符
addr   共享内存要映射实际物理的地址,一般情况下设置为空NULL,由Linux自行分配一块合适的地址

3.脱离共享内存

函数原型: int shmdt(void *addr)
函数功能: 从进程地址空间中断掉与共享内存的联系
头文件: <sys/ipc.h>  <sys/shm.h> <sys/types.h>
返回值: 成功时返回0,失败时返回-1
参数: void *addr 要断开的共享内存的映射地址

4.删除共享内存

函数原型: int shmctl(int shmid, int cmd, struct shmid_ds *buf)
函数功能: 对shmid对应的共享内存进行一定的操作
头文件: <sys/ipc.h>  <sys/shm.h> <sys/types.h>
返回值: 失败时返回-1,成功时根据不同操作返回不同的值
参数:
shmid 要控制的共享内存的id
cmd   决定执行什么样的操作,IPC_RMID  删除共享内存
buf:  获取linux描述共享内存的shmid_ds结构,基本用不上,一般让它为0

6、消息队列

消息队列就是一个消息的链表。而一条消息则可看作一个记录,具有特定的格式。进程可以向中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息

1.创建一个消息队列

函数原型: int msgget(key_t key, int msgflag)
函数功能: 创建一个消息队列
头文件: <sys/ipc.h>  <sys/types.h>  <sys/msg.h>
返回值: 创建成功返回消息队列ID,失败返回0
参数: key_t 键值,msgflag 打开标志 最重要的是IPC_CREAT

2.发送一个消息队列数据

函数原型: int msgsnd(int msgid,const void *ptr,size_t nbytes, int flag)
函数功能: 发送一条消息队列
头文件: <sys/ipc.h>  <sys/types.h>  <sys/msg.h>
返回值: 成功返回0,失败返回-1
参数:
msgid 消息队列ID
ptr   一条消息的指针特,具有如下结构
struct msgbuf
{
long mtype;    //消息类型,认为取1,2,3
char mtext[L];

size_t   发送数据的长度
flag     发送消息的标志

3.接收一个消息队列数据

函数原型: ssize_t msgrcv(int msgid,void *ptr,size_t nbytes, long msgtype,int flag)
函数功能: 接收一条消息队列
头文件: <sys/ipc.h>  <sys/types.h>  <sys/msg.h>
返回值: 成功返回0,失败返回实际读取到的字符数
参数:
msgid   消息队列ID
ptr     一条消息的指针
size_t   希望读取的字节数
long     接收数据的类型
如果等于0,则取队列中的第一条消息
        如果大于0,则取消息队列中的第一条符合类型的消息
        如果小于0,则取第一个比它绝对值小的最小类型的消息
flag    接收标志

4.对消息队列的操作

函数原型: int msgctl(int msqid, int cmd, struct msqid_ds *buf)
函数功能: 对消息队列进行操作
头文件: <sys/ipc.h>  <sys/types.h>  <sys/msg.h>
返回值: 操作成功0,操作失败返回-1
参数:
msqid   消息队列id
cmd     进行的操作,IPC_RMID为删除命令
buf     系统内核返回的信息
0 0
原创粉丝点击