初学Linux--进程间通信(管道、消息队列)

进程间通信是在不同的进程间进行信息交换的机制。一个复杂的应用系统,往往需要多个进程协同完成。Linux进程间通信机制主要包括:管道、信号、消息队列、共享内存、信号量、套接口……
管道:管道可以划分为普通管道和命名管道,普通管道用于亲缘间通信,命名管道在普通管道的基础上,通过给管道命名的方式,使管道变成文件系统中的管道文件,从而允许无亲缘关系进程间通过访问管道文件进行通信。
信号:用于通知进程发生某事,只要知道进程号就可以发送信号。
消息队列:消息的链表,进程可以向消息队列发送某种类型的消息,也可以从消息队列中读取指定类型的消息,消息内容可以根据需要自定义,从而客服信号承载量过小的问题
共享内存:在系统中的分配一块缓冲区,多个进程都可以访问该缓冲区。避免了内核空间与用户空间的切换,因此读写效率高。
信号量:也称信号灯,用于进程间的同步
套接字:可用于跨越主机边界的通信,是事实上的网络编程接口

消息队列、信号量、共享内存并称为进程间通信,简称IPC,IPC又可分为System V进程间通信和POSIX进程间通信

管道

概念及特点
管道是一个连接两个进程的连接器,管道是单向的,一端只能用于输入,一端只能用于输出,管道遵循“先入先出”,管道大小有限制,Linux系统下大小为4096字节。管道满时,写管道将阻塞,空时,读管道将阻塞。从本质上来看,Linux管道也是一种文件。
普通管道位于内存中,父进程创建一个管道,并将该管道传递给子进程。这样通过管道父进程与子进程便可以通信。
命名管道位于文件系统,只要可以访问文件系统的进程都可以通过管道的名字进行访问。所以命名管道可以实现无亲缘关系的两个进程间的通信处理。
管道编程
1.创建管道:创建管道的函数是pipe,该函数以一个两个成员的整形数据为参数。调用成功后第一个成员是用于读的文件描述符,第二个用于写。

#include<unistd.h>
int pipe(int _pipedes[2]);

_pipedes:整形数组。该数组将保存系统返回的管道描述符,_pipedes[0]用于读操作,_pipedes[1]用于写操作。
该函数可能的错误原因:
EFAULT:参数_pipedes无效
EMFILE:超过进程可打开的最大文件数量
ENFILE:已到达系统对打开文件数量的总限制
2.读写管道:管道创建成功后,分别返回读写管道描述符,可以用文件操作的相关函数对描述符进行读写,进行读写时需要注意几点:
- 如果写描述符关闭,读函数返回的读出字节数为0
- 如果读描述符关闭,写入操作将是无意义的,会返回SIGPIPE的错误。
- 向管道中写入时,如果管道已满,则写操作将阻塞直至管道有可用空间。
3.关闭管道:管道使用完后应该调用close关闭。由于一个管道有两个描述符,要分别关闭,另外,在多进程中,要注意管道描述符的引用次数,引用计数为0时,才能真正关闭。
4.管道I/O:在应用管道进行编程时,往往需要首先创建一个管道,再并发一个进程,父子进程通过管道进行数据交换,为了方便编程,标准I/O库提供了两个专用的I/O的函数:popen和pclose

#include<stdio.h>
FILE*popen(_const char*_command,_const char*_modes);
int pclose(FILE*_stream);

_command:要执行的命令串。
_modes:方式,r:返回的文件指针用于读,w:用于写。
popen实现的功能是:首先创建一个管道,然后fork一个进程,在子进程中通过exec系列函数加载要执行的程序,调用成功后,返回一个FILE*数据结构的指针。
命名管道编程
1.创建管道:在shell命令下有两种方式创建一个命名管道,分别是mknod和mkfifo。创建成功后可以通过ls命令看到新创建的管道文件。mknod 管道名称 pp是用于指明创建的节点类型是管道。mkfifo -m 权限 管道名称。LInux提供的创建管道的函数是mkfifo,这里mkfifo是一个函数,与上面的命令不同

#include<sys/stat.h>
int mkfifo(_const char*_path,_mode_t _mode);

_path:管道名称路径,类似于文件路径,可以用绝对路径,也可以用相对路径
_mode:新建管道的访问权限。
2.打开管道以及读写:管道创建成功后,要想使用管道,还需打开管道,打开管道的函数是open跟文件操作的打开函数是一样的,有两种打开方式:一种是阻塞;一种是非阻塞。缺省下是阻塞,在open时指定O_NONBLOCK,则将以非阻塞方式打开。
非阻塞方式下,对文件的只读打开将立即返回。而如果事先没有进程以只读方式打开管道,对管道的写方式将返回失败,error信息为ENXIO。
阻塞方式下,当打开一个管道用于读时,如果没有打开管道用于写的进程则打开操作会阻塞,知道一个进程用写的方式打开管道为止。同样当打开一个写的进程时,如果没有一个打开管道用于读的进程,则打开操作将阻塞,直到一个读的方式打开管道为止。

消息队列

LInux操作系统IPC机制支持不同的标准,包括system V和POSIX标准。目前大量应用都使用System V标准实现。
System V进程间通信概述
System V进程间通信主要包括消息队列、共享内存、信号量,简称IPC。使用这些IPC的基本过程包括创建、读写、删除、在使用IPC对象前需要指定一个关键字,并根据关键字创建一个IPC对象,在应用程序的进程内,根据创建IPC对象返回的标识符对该对象进行访问。

1.shell环境控制IPC:IPC对象一经创建,系统内核即为该对象分配相关数据结构。为方便对IPC对象的管理,Linux提供了专门的IPC控制命令,主要包括查看IPC对象信息的ipcs和删除IPC对象的ipcrm。ipcs [-asmq],-a:查看全部IPC对象信息,-q:查看消息队列信息,-m:查看共享内存信息,-s:查看信号量信息。ipcrm -[smq]或者ipcrm -[SMQ] Key-q/Q:删除消息队列,-m/M:删除共享内存,-s/S:删除信号量。如果指定smq,则用IPC的标识符作为输入,如果指定SMQ,则用IPC的键值作为输入。键值:在系统中是全局唯一的,表明该对象的键值。不同的IPC机制,其键值是可以重复的。标识符:对于同一键值的IPC对象,每重建一次,标识符都+1,到达系统规定的最大值后归零重新开始+1。

2.进程间通信关键字:为区别不同进程间通信的对象,Linux的每个IPC对象都有一个名字,称为键(key)。这个关键字是全局唯一,通过键,不同进程同样可以访问同意个IPC对象。进程间通信关键字是一个32位的长整数型数据,在使用过程中为了防止混淆和冲突,Linux提供了如下机制以产生唯一的关键字:1.创建IPC对象时,指定关键字为IPC_PRIVATE。以该参数创建IPC对象,系统会自动分配一个可用的关键字。通过该参数创建的IPC对象的关键字是0,所以无法在其他进程中通过关键字对该对象进行访问,只能通过返回的标识符进行访问。2.调用ftok时,产生一个唯一的关键字值,通过IPC进行通信的进程,只需要按照相同的参数调用ftok即可产生唯一的参数。通过该参数可有效的解决关键字的产生以及唯一性。

3.进程间通信标识符:Linux中对IP C对象的访问是通过标识符进行的。

消息队列基本概念
Linux的IPC机制为描述消息专门提供了一个结构

#include<sys/msg.h>
struct msgbuf{
long int mtype;//消息类型
char mtext[1];//消息内容
};

消息队列具有队列先进先出的基本特征,也具有其独特的性质:消息队列中的消息是有类型的,发送消息时,可指定其类型,收消息时,可按照消息类型获取数据。实际应用中,可以多个应用共用一个队列,按消息类型区分不同的队列。
消息队列编程
1.键值生成函数ftok:根据文件名和一个整形的变量(项目ID)生成一个唯一的键值,并且保证每次以相同的参数调用ftok返回的键值相同。

#include<sys/ipc.h>
key_t ftok(_const char*_pathname,int _proj_id);

2.创建消息队列:Linux下,实现创建消息队列功能的函数是msgget。该函数会返回一个已存在的消息队列的标识符。

#include<sys/msg.h>
int msgget(key_t _key,int _msgflg);

_key:键值有三种方式:1.直接指定一个键值;2.用ftok产生一个键值;3.用IPC_PRIVATE作为参数输入,将产生一个键值为零的新队列,进程只能用返回的标识符进行消息的传送与接收。
_msgflg:输入参数,标志和权限信息。
调用成功后,会返回一个msgqid_ds的数据结构用于管理消息队列。这个数据结构可以在程序中调用msgctl来获取内容。

3.消息发送:向队列中发送消息使用msgsnd函数。

#include<sys/msg.h>
int msgsnd(int _msgid,_const void*_msgp,size_t_msgsz,int _msgflg);

_msgid:输入参数,消息队列的标识符,由msgget函数返回标识符
_msgp:消息结构指针。
_msgsz:消息长度。
_msgflg:发送消息可选标志。选择了IPC_NOWAIT便是非阻塞的。

4.消息接收:

#include<sys/msg.h>
int msgrcv(int_msgid,void *_msgp,size_t_msgsz,long int_msgtyp,int _msgflg);

5.控制消息队列:IPC机制提供了函数对消息队列的控制结构即msqid_ds进行控制

#include<sys/msg.h>
int msgctl(int _msgid,int_cmd,struct msqid_ds*_buf);

参考《Linux编程从入门到精通》

猜你喜欢

转载自blog.csdn.net/Han_L/article/details/77822108