Linux进程间通信

来源:互联网 发布:把人变老的软件 编辑:程序博客网 时间:2024/06/01 22:32

进程间通信的目的

1、数据传输:一个进程需要将它的数据发送给另一个进程。
2、资源共享:多个进程之间共享同样的资源。
3、通知事件:一个进程需要向另一个或一组进程发送信息,通知它们发生了某种事情。
4、进程控制:有些进程希望完全控制另一个进程。此时控制进程能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。

管道

管道是单向的、先进先出的、无结构的、固定大小的字节流。
创建管道:调用pipe函数在内核开辟一块缓冲区,称为管道,pipe函数调用成功返回1,失败返回0。
1、它有一个读端一个写端。
2、通过filedes参数传出给用户程序两个文件描述符:filedes[0]指向读端,filedes[1]指向写端。
3、向这个文件读写数据其实是在读写内核缓冲区。
4、单个管道只能单向通信,一端用于读,而另一端用于写。如果要实现进程双向通信,必须创建一对管道。
管道的局限性:
     1、历史上,管道是半全双工,(即数据只能在一个方向上流动,只能是一端写,一端读)。
     2、只能在有公共祖先的进程之间使用(在父子进程或兄弟进程间使用)——普通管道。
     3、读数据的同时也将数据从管道移走,因此,管道不能用来对多个接受者广播数据。
     4、管道中的数据被当做字节流,因此无法识别信息的边界。
     5、如果一个管道有多个读进程,那么写进程不能发送数据到指定的读进程,同样,如果有多个写进程,那么没有办法判断是哪一个发送数据。
命名管道:可以在不相关的进程之间通信。
创建命名管道的系统函数有两个:mkfifo和mknod,调用成功返回0,失败返回-1,两个函数的原型如下:


通信:
     1、父进程调用pipe函数开辟管道,得到两个文件描述符分别指向管道的两端。
     2、父进程调用fork函数创建子进程,同样子进程也有两个文件描述符指向该管道的两端。
     3、父进程关闭读端,子进程关闭写端,父进程可以往管道里写,子进程可以从管道中读,管道是用环形队列实现的,数据从写端流入读端流出,这样就实现进程间通信。

Linux必须保证管道的写和读步调一致:linux使用锁、等待队列、和信号实现同步。

消息队列

消息队列提供从一个进程到另一个进程发送数据块的方法。
消息队列与管道的区别:
         1、消息队列是基于消息的,管道是基于字节流的。
         2、消息队列发送消息来避免管道的同步和阻塞问题。
         3、消息队列的读取不一定是先进先出。
         4、每个消息队列的最大长度有上限,给定消息总的字节数有上限,消息队列总消息数有上限。
内核为每个IPC对象维护一个数据结构,都包括ipc_perm数据结构。(消息队列、信号量、共享内存都有这个数据结构)。
struct ipc_perm
{
     __kernel_key_t key; //IPC对象的键值
     __kernel_uid_t uid; //进程实际用户ID
     __kernel_gid_t gid; //进程实际用户组ID
     __kernel_uid_t cuid; //创建者进程ID
     __kernel_gid_t cgid; //创建者进程组ID
     __kernel_mode_t mode; //访问模式
     unsigned short seq; //序号
};
消息队列就是一个消息的一个链表,允许一个或多个进程向它写消息,一个或多个进程从中读消息。
Linux提供消息队列的操作:
1、创建或获得消息队列:
函数原型:int sys_msgget (key_t key, int msgflg);
参数:key:是一个键值,可以认为是一个端口号,也可以由函数ftok生成。
           msgflg:是一个标志。
           msgflg:
                    IPC_CREAT:如果IPC不存在,则创建一个IPC资源,否则打开操作。
                    IPC_EXCL:  IPC_CREAT和IPC_EXCL可以保证所得对象是新建的,而不是打开的已有对象。
工作过程如下:
1) 如果key == IPC_PRIVATE,则申请一块内存,创建一个新的消息队列( 数据结构msqid_ds),将其初始化后加入到msgque向量表中的某个空位置处,返回标识符。
2) 在msgque向量表中找键值为key的 消息队列,如果没有找到,结果有二:
a、 msgflg表示不创建新的队列,则错误返回。
b、 msgflg表示要创建新的队列,则创建新 消息队列,创建过程如1)。
3) 如果在msgque向量表中找到了键值为key的 消息队列,则有以下情况:
a、如果msgflg表示一定要创建新的 消息队列而且不允许有相同键值的队列存在,则错误返回。
b、如果找到的队列是不能用的或已损坏的队列,则错误返回。
c、认证和存取权限检查,如果该队列不允许msgflg要求的存取,则错误返回。
d、正常,返回队列的 标识符。
2、向队列读/写消息
函数原型:

msgsnd:
该函数做如下工作:
1) 该 消息队列在向量msgque中的索引是id = (unsigned int) msqid % MSGMNI,认证检查(权限、模式),合法性检查(类型、大小等)。
2) 如果 队列已满,以可中断等待状态(TASK_INTERRUPTIBLE)将当前进程挂起在wwait 等待队列上。
3) 申请一块空间,大小为一个消息 数据结构加上消息大小,在其上创建一个消息数据结构struct msg,将消息缓冲区中的消息内容拷贝到该内存块中消息头的后面(从 用户空间拷贝到 内核空间)。
4) 将消息数据结构加入到 消息队列的队尾,修改队列的相应参数(大小等)。
5) 唤醒在该 消息队列的rwait进程队列上等待读的进程。
6) 返回。
msgrcv:
该函数做如下工作:
1) 该消息队列在向量msgque中的索引是id = (unsigned int) msqid % MSGMNI,认证检查(权限、模式),合法性检查。
2)根据msgtyp和msgflg搜索 消息队列,情况有二:
3)如果找不到所要的消息,则以可中断等待状态(TASK_INTERRUPTIBLE)将当前进程挂起在rwait等待队列上。
4)如果找到所要的消息,则将消息从 队列中摘下,调整队列参数,唤醒该 消息队列的wwait进程队列上等待写的进程,将消息内容拷贝到 用户空间的消息缓冲区msgp中,释放内核中该消息所占用的空间,返回。

信号量

信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。
使用信号量的原因:为了防止出现多个程序同时访问一个共享资源引发的一系列问题。任一时刻只能有一个执行线程访问代码的临界区域。
信号量只能进行两种操作等待和发送信号即P(sv)和V(sv)。

共享内存

通常由一个进程创建。其他进程对这个内存进行读写,得到共享内存通常是通过内存映射。
linux本身无法对其同步,需要程序自己来对共享内存做出同步计算,何种同步很多时候就是用信号量实现的。

效率比较



原创粉丝点击