20155301 滕树晨linux基础——linux进程间通信(IPC)机制总结

共享内存

  • 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间(这里的地址空间具体是哪个地方?)中。其他进程可以将同一段共享内存连接到自己的地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是malloc分配的一样。如果一个进程向共享内存中写入了数据,所做的改动将立刻被其他进程看到

  • 共享内存是IPC最快捷的方式,因为共享内存方式的通信没有中间过程,而管道、消息队列等方式则是需要将数据通过中间机制进行转换。共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是同一块的物理空间,仅仅映射到各进程的地址不同而已,因此不需要进行复制,可以直接使用此段空间。

  • 注意:共享内存本身并没有同步机制,需要程序员自己控制。

  • 共享内存的头文件

#include <sys/types.h>#include <sys/stat.h>#include <sys/shm.h>
  • 结构shmid_ds结构体
strcut shmid_ds{struct ipc_perm shm_perm;size_t shm_segsz;time_t shm_atime;time_t shm_dtime;......
}
  • 共享内存函数定义:

    int shmget(key_key,size_t size,int shmflg); //shmget函数用来创建一个新的共享内存段,或者访问一个现有的共享内存段(不同进程只要key值相同即可访问同一共享内存段)。第一个参数key是ftok生成的键值,第二个参数size为共享内存的大小,第三个参数sem_flags是打开共享内存的方式。

    eg.int shmid = shmget(key, 1024, IPC_CREATE | IPC_EXCL | 0666);//第三个参数参考消息队列int msgget

    (key_t key,int msgflag);

    void shmat(int shm_id,const void shm_addr,int shmflg); //shmat函数通过shm_id将共享内存连接到进程的地址空间中。第二个参数可以由用户指定共享内存映射到进程空间的地址,shm_addr如果为0,则由内核试着查找一个未映射的区域。返回值为共享内存映射的地址。  
    eg.char shms = (char )shmat(shmid, 0, 0);//shmid由shmget获得

    int shmdt(const void *shm_addr); //shmdt函数将共享内存从当前进程中分离。 参数为共享内存映射的地址。  
    eg.shmdt(shms);

    int shmctl(int shm_id,int cmd,struct shmid_ds *buf);//shmctl函数是控制函数,使用方法和消息队列msgctl()函数调用完全类似。参数一shm_id是共享内存的句柄,cmd是向共享内存发送的命令,最后一个参数buf是向共享内存发送命令的参数。

管道

管道实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。

  • 管道的特点:

    1、管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

    2、只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。当父进程与使用fork创建的子进程直接通信时,发送数据的进程关闭读端,接受数据的进程关闭写端。

    3、单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

    4、数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

  • 管道的实现机制:

    管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

    管道只能在本地计算机中使用,而不可用于网络间的通信。

    pipe函数原型:

#include <unistd.h>
int pipe(int file_descriptor[2]);//建立管道,该函数在数组上填上两个新的文件描述符后返回0,失败返回-1。
eg.int fd[2]
int result = pipe(fd);  

通过使用底层的read和write调用来访问数据。 向file_descriptor[1]写数据,从file_descriptor[0]中读数据。写入与读取的顺序原则是先进先出

  • 管道读写规则

    当没有数据可读时:

    O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。

    O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

    当管道满的时候

    O_NONBLOCK disable:write调用阻塞,直到有进程读走数据

    O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,则read返回0

如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE

当要写入的数据量不大于PIPE_BUF(Posix.1要求PIPE_BUF至少512字节)时,linux将保证写入的原子性。

当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

## 命名管道(FIFO)

命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了管道的弊端,他可以允许没有亲缘关系的进程间通信。

创建管道的两个系统调用原型:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename,mode_t mode); //建立一个名字为filename的命名管道,参数mode为该文件的权限(mode%~umask),若成功则返回0,否则返回-1,错误原因存于errno中。
eg.mkfifo( "/tmp/cmd_pipe", S_IFIFO | 0666 );  

具体操作方法只要创建了一个命名管道然后就可以使用open、read、write等系统调用来操作。创建可以手工创建或者程序中创建。

int mknod(const char *path, mode_t mode, dev_t dev); //第一个参数表示你要创建的文件的名称,第二个参数表示文件类型,第三个参数表示该文件对应的设备文件的设备号。只有当文件类型为 S_IFCHR 或 S_IFBLK 的时候该文件才有设备号,创建普通文件时传入0即可。
eg.mknod(FIFO_FILE,S_IFIFO|0666,0);    
  • 管道和命名管道的区别:

    对于命名管道FIFO来说,IO操作和普通管道IO操作基本一样,但是两者有一个主要的区别,在命名管道中,管道可以是事先已经创建好的,比如我们在命令行下执行

    mkfifo myfifo

    就是创建一个命名通道,我们必须用open函数来显示地建立连接到管道的通道,而在管道中,管道已经在主进程里创建好了,然后在fork时直接复制相关数据或者是用exec创建的新进程时把管道的文件描述符当参数传递进去。

    一般来说FIFO和PIPE一样总是处于阻塞状态。也就是说如果命名管道FIFO打开时设置了读权限,则读进程将一直阻塞,一直到其他进程打开该FIFO并向管道写入数据。这个阻塞动作反过来也是成立的。如果不希望命名管道操作的时候发生阻塞,可以在open的时候使用O_NONBLOCK标志,以关闭默认的阻塞操作。

信号

信号机制是unix系统中最为古老的进程之间的通信机制,用于一个或几个进程之间传递异步信号。信号可以有各种异步事件产生,比如键盘中断等。shell也可以使用信号将作业控制命令传递给它的子进程。

  • 使用方法定义:
#include <sys/types.h>
#include <signal.h>
void (*signal(int sig,void (*func)(int)))(int); //用于截取系统信号,第一个参数为信号,第二个参数为对此信号挂接用户自己的处理函数指针。返回值为以前信号处理程序的指针。
eg.int ret = signal(SIGSTOP, sig_handle);

由于signal不够健壮,推荐使用sigaction函数。

int kill(pid_t pid,int sig); //kill函数向进程号为pid的进程发送信号,信号值为sig。当pid为0时,向当前系统的所有进程发送信号sig。
int raise(int sig);//向当前进程中自举一个信号sig, 即向当前进程发送信号。
#include <unistd.h>
unsigned int alarm(unsigned int seconds); //alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。使用alarm函数的时候要注意alarm函数的覆盖性,即在一个进程中采用一次alarm函数则该进程之前的alarm函数将失效。
int pause(void); //使调用进程(或线程)睡眠状态,直到接收到信号,要么终止,或导致它调用一个信号捕获函数。  

消息队列

消息队列是内核地址空间中的内部链表,通过linux内核在各个进程直接传递内容,消息顺序地发送到消息队列中,并以几种不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地进行识别。内核中的消息队列是通过IPC的标识符来区别,不同的消息队列直接是相互独立的。每个消息队列中的消息,又构成一个独立的链表。

消息队列克服了信号承载信息量少,管道只能承载无格式字符流。

  • 消息队列头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/msg.h>   
  • 消息缓冲区结构
struct msgbuf{  long mtype;  char mtext[1];//柔性数组
}  

在结构中有两个成员,mtype为消息类型,用户可以给某个消息设定一个类型,可以在消息队列中正确地发送和接受自己的消息。mtext为消息数据,采用柔性数组,用户可以重新定义msgbuf结构。例如:

struct msgbuf{  long mtype;  char mtext[1];//柔性数组
}  

当然用户不可随意定义msgbuf结构,因为在linux中消息的大小是有限制的,在linux/msg.h中定义如下:
#define MSGMAX 8192

消息总的大小不能超过8192个字节,包括mtype成员(4个字节)。

  • msqid_ds内核数据结构。
<div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">struct msgid_ds{</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">   struct ipc_perm msg_perm{</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">   time_t msg_stime;</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">   time_t msg_rtime;</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">   time_t msg_ctime;</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">   unsigned long _msg_cbuyes;</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">    ..........</div><div style="font-family: 微软雅黑; font-size: 14px; line-height: 21px;">   };</div>

Linux内核中,每个消息队列都维护一个结构体,此结构体保存着消息队列当前状态信息,该结构体在头文件linux/msg.h中定义。

  • ipc_perm内核数据结构
struct ipc_perm{  key_t key;  uid_t uid;  gid_t gid;  .......
};  

结构体ipc_perm保存着消息队列的一些重要的信息,比如说消息队列关联的键值,消息队列的用户id组id等。它定义在头文件linux/ipc.h中。

常用函数:

key_t ftok( const char * fname, int id );//参数一为目录名称, 参数二为id。如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
eg.key_t key = key =ftok(".", 1);
int msgget(key_t key,int msgflag); //msgget用来创建和访问一个消息队列。程序必须提供一个键值来命名特定的消息队列。
eg.int msg_id = msgget(key, IPC_CREATE | IPC_EXCL | 0x0666);//根据关键字创建一个新的队列(IPC_CREATE),如果队列存在则出错(IPC_EXCL),拥有对文件的读写执行权限(0666)。
int msgsnd(int msgid,const void *msgptr,size_t msg_sz,int msgflg); //msgsnd函数允许我们把一条消息添加到消息队列中。msgptr只想准备发送消息的指针,指针结构体必须以一个长整型变量开始。
eg.struct msgmbuf{int mtype;char mtext[10];
};
struct msgmbuf msg_mbuf;
msg_mbuf.mtype = 10;//消息大小10字节
memcpy(msg_mbuf.mtext, "测试消息", sizeof("测试消息"));
int ret = msgsnd(msg_id, &msg_mbuf, sizeof("测试消息"), IPC_NOWAIT);
int msgrcv(int msgid, void *msgptr, size_t msg_sz, long int msgtype, int msgflg); //msgrcv可以通过msqid对指定消息队列进行接收操作。第二个参数为消息缓冲区变量地址,第三个参数为消息缓冲区结构大小,但是不包括mtype成员长度,第四个参数为mtype指定从队列中获取的消息类型。
eg.int ret = msgrcv(msg_id, &msg_mbuf, 10, 10, IPC_NOWAIT | MSG_NOERROR);
int msgctl(int msqid,int cmd,struct msqid_ds *buf); //msgctl函数主要是一些控制如删除消息队列等操作。 cmd值如下:
IPC_STAT:获取队列的msgid_ds结构,并把它存到buf指向的地址。
IPC_SET:将队列的msgid_ds设置为buf指向的msgid_ds。
IPC_RMID:内核删除消息队列,最后一项填NULL, 执行操作后,内核会把消息队列从系统中删除。
  • 消息队列的本质

    Linux的消息队列(queue)实质上是一个链表,它有消息队列标识符(queue ID)。 msgget创建一个新队列或打开一个存在的队列;msgsnd向队列末端添加一条新消息;msgrcv从队列中取消息, 取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息。

转载于:https://www.cnblogs.com/fengxingck/p/8012620.html

20155301 滕树晨linux基础——linux进程间通信(IPC)机制总结相关推荐

  1. linux基础——linux进程间通信(IPC)机制总结

    原 linux基础--linux进程间通信(IPC)机制总结 2016年07月23日 21:33:12 yexz 阅读数:18224更多 <div class="tags-box sp ...

  2. Linux基础入门--进程间通信--共享内存

    Linux基础入门--进程间通信--共享内存 1.共享内存IPC原理 2.共享内存管理 1.共享内存IPC原理 共享内存进程间通信机制主要用于实现进程间大量的数据传输,共享内存是在内存单独开辟的一段内 ...

  3. 视频教程-赵强老师:大数据从入门到精通(1)Linux基础-Linux

    赵强老师:大数据从入门到精通(1)Linux基础 毕业于清华大学,拥有超过13年的工作经验. Oracle认证讲师,拥有6年以上授课经验.精通Oracle数据库.中间(Weblogic)和大数据Had ...

  4. Linux 进程间通信-IPC 机制

    Linux 平台上在 Kernel 协调下完成进程之间的相互通信,有多种进程间通信 -- Inter Process Communication(IPC)方式. 1. IPC 分类 按照功能用途来看有 ...

  5. linux常用删除空文件夹,Linux基础 linux系统中的批量删除文件与空文件删除的命令介绍...

    Linux基础教程linux系统中的批量删除文件与空文件删除的命令介绍 Linux资料下面删除文件或者目录命令rm(remove): Linux培训功能说明:删除文件或目录. 语 法:rm [-dfi ...

  6. Linux中传统的IPC机制

    介绍 IPC全称为Inter-Process Communication,含义为进程间通信,是指两个进程之间进行数据交换的过程.IPC不是Android中所独有的,任何一个操作系统都需要有相应的IPC ...

  7. Linux基础——linux和gcc的介绍

    一. 为什么要用linux? 我的建议是:勇敢地迈出第一步,安装ubuntu,并且按着一本书看完linux的基础操作,你就会明白你对它的需求,或者说是它对你的吸引力了.  -------------- ...

  8. Linux基础——Linux 基本指令 ls 和 cd

    文章目录 1.前言 2.cd 指令 1.返回上一级目录 2.去往子文件夹 3.返回你刚刚所在的目录 4.向上返回两次 5.去往 Home 6.去往电脑任何地方, 你需要的是一个绝对路径 3.ls 指令 ...

  9. linux基础 linux命令跟踪 strace命令

    原文: (909条消息) Linux之strace命令_风华浪浪的博客-CSDN博客_linux strace trace :监控程序的执行状况 在linux 空间下,运行一个程序时,操作系统会将应用 ...

最新文章

  1. 一览群智胡健:在中国完全照搬Palantir模式,这不现实
  2. 多区域显示(8)-透明花边
  3. springboot配置
  4. bzoj1935: [Shoi2007]Tree 园丁的烦恼
  5. 前端JavaScripts
  6. SonarQube系列一、Linux安装与部署
  7. Visual Studio 2017 新功能(上)
  8. 可输入可下拉的输入选择框
  9. MATLAB-典型动态系统建模与仿真
  10. PS动感映像插件ImageMotion 1.3中文汉化版
  11. symbian 串行通信
  12. 解决登录雅虎邮箱提示您在所用浏览器上启用Javascript 功能
  13. 成语接龙 你看过这么拽的吗?
  14. 闪存卡提示格式化怎么办?里面的数据怎么恢复
  15. ES5 标准对象说明
  16. 好用的项目管理工具,选择Tracup的N种理由
  17. 扫地机器人系统,主要划分为哪几个模块?
  18. 车载网络技术——CAN总线基础
  19. HNUCM 1366 绿地装饰解题报告 (模拟)
  20. windows下安装ppocr服务 paddlehub方式

热门文章

  1. 1202: [HNOI2005]狡猾的商人
  2. 使用Java基于数据流直接抽取word文本
  3. 浦发银行(600000.sh):与中国移动珠联璧合联姻
  4. 联想笔记本降万元抢学生市场,兄弟们快抢啊
  5. 敏捷开发第二阶段个人(二)
  6. CSS3 background-size图片自适应
  7. 《SolidWorks 2017中文版机械设计从入门到精通)》——1.4 操作环境设置
  8. 使用maven下载依赖包及maven常见问题汇总
  9. linux使用wget下载https开头url的文件
  10. 玩转正则之highlight高亮