操作系统与网络 (7. 进程间通信IPC)

愿你一生努力,一生被爱。
最想要的都拥有,得不到的都释怀!

7. 进程间通信IPC

7.1 IPC介绍
7.1.1 定义
  • 简单来说就是进程间的沟通交流。
7.1.2 实现原理
  • 让多个进程通过访问到相同的缓冲区来实现通信,Linux 下遵循一切皆文件的思想。
7.1.3 本质
  • 内核中的一块缓冲区。
7.1.4 操作系统为什么要为用户提供进程间通信方式
  • (1)进程的独立性(每个进程都操作的是自己的虚拟地址空间中的虚拟地址,无法访问到别人的地址,因此无法直接通信);
  • (2)IPC的应用场景不同,因此提供了几种不同的IPC方式。
7.1.5 进程间通信的目的
  • (1)数据传输:一个进程需要将它的数据发送给另一个进程;
  • (2)资源共享:多个进程之间共享同样的资源;
  • (3)通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • (4)进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
7.1.6 IPC分类
  • (1)管道
  • (2)System V IPC
  • (3)POSIX IPC
7.2 通信方式
7.2.1 管道(实现数据传输)

(1)分类
匿名管道pipe

  • 定义
    只能用于具有亲缘关系的进程间通信,子进程通过复制父进程的文件描述符获取管道的操作句柄。
  • 实现原理
    其实就是创建了一个子进程,子进程复制了父进程的描述符表,因此也就具有了两个描述符,并且他们指向的是同一个管道,这时候因为他们都能访问到这个管道,就可以实现进程间通信了。
  • 接口实现
    原型:int pipe(int fd[2]);
    参数:fd 文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
    返回值:成功返回0,失败返回错误代码-1;
  • 说明
    一个进程创建匿名管道,操作系统在内核中重建一块缓冲区,并且返回两个文件描述符的作为管道的操作句柄(一个用于读,一个用于写,方向的选择权交给用户,但是这个缓冲区在内核中没有标识)。

命名管道fifo

  • 定义
    命名管道可以应用于同一主机上的任意进程间通信,是一种特殊类型的文件即文件系统可见。
  • 实现原理
    一个进程创建命名管道,操作系统在内核中创建一块缓冲区,并且返回两个文件的描述符作为管道的操作句柄,但是匿名管道在内核中这块缓冲区中是有标识的,意味着所有的进程可以通过这个标识找到缓冲区实现通信;
    命名管道的标识实际上是一个文件,可见于文件系统,意味着所有进程都可以通过打开文件进而访问到内核中的缓冲区。
  • 接口实现
    从命令行创建
    $ mkfifo filename;
    int mkfifo(const char* filename, mode_t mode);路径,权限

(2)本质
内核中的一块缓冲区
(3)特性

  • 管道的读写特性;

  • 半双工通信(有选择的单向通信);

  • 自带同步与互斥(内核会对管道操作进行同步与互斥);

    同步:保证操作的时序合理性;
    互斥:保证操作在同一时间的唯一性;

  • 提供流式服务,传输灵活,但是存在粘包问题(即数据之间没有明显的间隔);

  • 生命周期随进程, 一般而言,进程退出,管道释放;

(4)管道的读写规则

  • 当管道没有数据可读时
    如果描述符是默认的阻塞属性,读取将会阻塞挂起等待,直到管道有数据;
    如果描述符被设置为非阻塞属性,读取操作将不具备条件,直接报错返回EAGAIN;
  • 当管道数据满的时候
    如果描述符是默认的阻塞属性,写入操作将会阻塞挂起等待,直到有数据被取走;
    如果描述符被设置为非阻塞属性,写入操作将不具备条件,直接报错返回EAGAIN;
  • 如果所有管道写端对应的文件描述符被关闭,则read返回0;
  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,竟而可能导致write进程退出;
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性;
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
7.2.2 共享内存(实现数据共享)

(1)本质

  • 开辟一块物理内存,映射到各个进程的虚拟地址空间(通信进程都可访问)。

(2)最快的IPC

  • 共享内存是直接将一块物理内存映射到虚拟地址空间中,因此进行数据传输的事就相较于其他通信方式,少了两步用户态与内核态之间数据拷贝的过程,因此共享内存是最快的IPC。

(3)操作流程(实现原理)

  • 开辟共享内存
    在物理内存中开辟一块内存空间;
  • 建立映射关系
    将这块内存空间通过页表映射到进程的虚拟地址空间中;
  • 内存的数据操作
    进程可以直接通过进程虚拟地址访问这块物理内存进行操作(若多个进程映射到同一块物理内存,就可以实现相互通信);
  • 解除映射关系
  • 删除共享内存

(4)共享内存函数
1. shmget函数

  • 功能:用来创建共享内存
  • 原型:int shmget(key_t key, size_t size, int shmflg);
  • 参数
    key:这个共享内存段名字
    size:共享内存大小
    shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。
  • 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。

2. shmat函数

  • 功能:将共享内存段连接到进程地址空间
  • 原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 参数
    shmid:共享内存标识
    shmaddr:指定连接的地址
    shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
  • 返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

3. shmdt函数

  • 功能:将共享内存段与当前进程脱离
  • 原型:int shmdt(const void *shmaddr);
  • 参数
    shmaddr:由shmat所返回的指针
  • 返回值:成功返回0;失败返回-1
  • 注意:将共享内存段与当前进程脱离不等于删除共享内存段

4. shmctl函数

  • 功能:用于控制共享内存
  • 原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 参数
    shmid:由shmget返回的共享内存标识码
    cmd:将要采取的动作(有三个可取值)
    buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
  • 返回值:成功返回0;失败返回-1
7.2.3消息队列(数据块的传输)

(1)本质

  • 操作系统在内核为我们创建的一个队列,多个进程可以通过向队列中添加结点或获取结点来记录数据传输。

(2)特性

  • 有类型的数据块
  • 消息队列是一个全双工通信,可读可写(可以发送数据,也可以接受数据)
  • 生命周期随随内核
    创建消息队列(msgget)
    发送数据、接受数据 (msgsnd/msgrcv)
    释放消息队列 (msgctl)

(3)查看IPC

  • ipcs -m 查看共享内存
  • ipcs -s 查看信号量
  • ipcs -q 查看消息队列
7.2.4 信号量(实现进程间的相互控制,实现同步互斥)

(1)本质

  • 是内核中的一个具有等待队列的计时器(资源计数,统计现在有多少资源,用于判断是
    否能够进行操作)
  • 信号量实际上也是一个临界资源
  • 原子操作

(2)功能

  • 实现进程间的同步与互斥;

(3)实现原理

  • 在对资源进行访问之前,先判断信号量计数(是否有资源能够操作);
  • 若计数<=0,则进行等待(等待别人创建资源),计数-1;等待在等待队列上;
  • 若计数>0,则直接返回(按照程序流程就可以直接操作资源了),计数-1,其他进程创建了资源,计数+1,并且唤醒等待队列上的那些进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值