Linux系统编程---4(进程间通信IPC,管道)

进程间通信目的

  1. 数据传输:一个进程需要将它的数据发送给另一个进程
  2. 资源共享:多个进程之间共享同样的资源。
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变

IPC

linux系统下7中文件类型

  1. -- 文件
  2. d 目录
  3. l 符号链接
    伪文件:(不占用内存)
  4. s 套接字
  5. b 块设备
  6. c 字符设备
  7. p 管道
    Linux 环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另 一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区, 进程 1 把数据从用户空间拷到内核缓冲区,进程2 再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通 信(IPC,InterProcessCommunication)。
    在这里插入图片描述
    在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套 接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信 方式有:
  8. 管道 (使用最简单)
    pipe(管道一般读写行为)
    fifo(有名管道,用于非血缘关系)
  9. 信号 (开销最小)
  10. 共享映射区 (无血缘关系)
  11. 本地套接字 (最稳定)

管道

管道的概念:

管道是一种最基本的 IPC 机制,作用于有血缘关系的进程之间,完成数据传递。调用 pipe 系统函数即可创建一 个管道。

管道特质:

  1. 其本质是一个伪文件(实为内核缓冲区)
  2. 由两个文件描述符引用,一个表示读端,一个表示写端。
  3. 规定数据从管道的写端流入管道,从读端流出。

管道的原理:

管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。

管道的局限性:

  1. 数据自己读不能自己写。
  2. 数据一旦被读走,便不在管道中存在,不可反复读取。
  3. 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动
  4. 只能在有公共祖先的进程间使用管道

常见的通信方式

  1. 单工通信,只能收不能发
  2. 半双工通信,数据只能在一个方向上流动
  3. 全双工通信。

管道的特点

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创 建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  2. 管道提供流式服务
  3. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  4. 一般而言,内核会对管道操作进行同步与互斥
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

pipe 函数

int  pipe (int  pipefd[2] ); 成功:0;失败:-1,设置 errno 

函数调用成功返回 r/w 两个文件描述符。无需 open,但需手动 close。规定:fd[0] → r; fd[1] → w,就像 0 对应标准输入,1 对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢? 通常可以采用如下步骤:
在这里插入图片描述

  1. 父进程调用 pipe 函数创建管道,得到两个文件描述符 fd[0]、fd[1]指向管道的读端和写端。

  2. 父进程调用 fork 创建子进程,那么子进程也有两个文件描述符指向同一管道。

  3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。 由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
    在这里插入图片描述

     #include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<string.h>int main(void){int fd[2];pid_t pid;int ret=pipe(fd);if(ret==-1){   perror("pipe error:");exit(1);}   pid = fork();if(pid==-1){perror("perror error:");exit(1);}else if(pid==0){    //子进程  读数据 close(fd[1]);//关闭管道的写端char buf[1024];ret=read(fd[0],buf,sizeof(buf));if(ret == 0){ printf("----\n");}   write(STDOUT_FILENO,buf,ret);}else{                   //父进程写数据sleep(1);                                                                   close(fd[0]);//关掉管道的读端write(fd[1],"hello pipe\n",strlen("hello pipe\n"));}   }
    

在这里插入图片描述

管道的读写行为

使用管道需要注意以下 4 种特殊情况(假设都是阻塞 I/O 操作,没有设置 O_NONBLOCK 标志) :

  1. 如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为 0),而仍然有进程从管道的读端读数 据,那么管道中剩余的数据都被读取后,再次 read 会返回 0,就像读到文件末尾一样。
  2. 如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于 0),而持有管道写端的进程也没有向管 道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次 read 会阻塞,直到管道中有 数据可读了才读取数据并返回。
  3. 如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为 0),这时有进程向管道的写端 write, 那么该进程会收到信号 SIGPIPE,通常会导致进程异常终止。当然也可以对 SIGPIPE 信号实施捕捉,不终止进程。具 体方法信号章节详细介绍。
  4. 如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于 0),而持有管道读端的进程也没有从管 道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次 write 会阻塞,直到管道中有空位置了才写 入数据并返回。

总结

读管道

  1. 管道中有数据,read 返回实际读到的字节数
  2. 管道中无数据:
    (1) 管道写端被全部关闭,read 返回 0(好像读到文件结尾)
    (2) 写端没有全部被关闭,read 阻塞等待(不久的将来可能有数据递达,此时会让出 cpu)

写管道

  1. 管道读端全部被关闭, 进程异常终止(也可使用捕捉 SIGPIPE 信号,使进程不终止)
  2. 管道读端没有全部关闭:
    (1) 管道已满,write 阻塞。
    (2) 管道未满,write 将数据写入,并返回实际写入的字节数。

管道缓冲区大小

可以使用 ulimit–a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为:

pipe size     (512bytes,-p)8

也可以使用 fpathconf 函数,借助参数 选项来查看。使用该宏应引入头文件<unistd.h>

long  fpathconf(int  fd,int  name); 成功:返回管道的大小 失败:-1,设置 errno

管道的优劣

优点:简单,相比信号,套接字实现进程间通信,简单很多。
缺点:

  1. 只能单向通信,双向通信需建立两个管道。
  2. 只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用 fifo 有名管道解决。

FIFO

FIFO 常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过 FIFO,不相 关的进程也能交换数据。
FIFO 是 Linux 基础文件类型中的一种。但,FIFO 文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各 进程可以打开这个文件进行 read/write,实际上是在读写内核通道,这样就实现了进程间通信。
创建方式:

  1. 命令:mkfifo 管道名
  2. 库函数:int mkfifo(const char*pathname, mode_t mode); 成功:0; 失败:-1 一旦使用 mkfifo 创建了一个 FIFO,就可以使用 open 打开它,常见的文件 I/O 函数都可用于 fifo。如: close、 read、 write、unlink 等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

冲突域 广播域简单解释

网络互连设备可以将网络划分为不同的冲突域、广播域。但是&#xff0c;由于不同的网络互连设备可能工作在OSI模型的不同层次上。因此&#xff0c;它们划分冲突域、广播域的效果也就各不相同。如中继器工作在物理层&#xff0c;网桥和交换机工作在数据链路层&#xff0c;路由器工…

Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)

共享存储映射 文件进程间通信 使用文件也可以完成 IPC&#xff0c;理论依据是&#xff0c;fork 后&#xff0c;父子进程共享文件描述符。也就共享打开的文件。 编程&#xff1a;父子进程共享打开的文件。借助文件进行进程间通信。 测试代码 /*** 父子进程共享打开的文件描述…

变量的存取

一、预备知识―程序的内存分配 一个由c/C编译的程序占用的内存分为以下几个部分 1、栈区&#xff08;stack&#xff09;― 由编译器自动分配释放 &#xff0c;存放函数的参数值&#xff0c;局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区&#xff08;heap&#xff…

Linux系统编程---6(信号的机制,信号4要素,Linu常规信号表,定时器)

信号的概念 信号在我们的生活中随处可见&#xff0c; 如&#xff1a;古代战争中摔杯为号&#xff1b;现代战争中的信号弹&#xff1b;体育比赛中使用的信号枪… 他们都有共性&#xff1a; 简单不能携带大量信息&#xff0c;只能带一个标志。满足某个特设条件才发送。 Unix 早…

Linux系统编程----7(信号集,信号屏蔽,信号捕捉)

信号集操作函数 内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字 mask 可以影响未决信号集。而我们可以在应 用程序中自定义 set 来改变 mask。已达到屏蔽指定信号的目的。 信号集设定 sigset_t set; //typedef unsigned long sigset_t;int sigemptyset(sigset_t…

Linux系统编程---8(全局变量异步I/O,可重入函数)

全局变量异步 I/O 分析如下父子进程交替 数数 程序。当捕捉函数里面的 sleep 取消&#xff0c;程序即会出现问题。请分析原因。 #include<stdio.h> #include<signal.h> #include<unistd.h> #include<stdlib.h>intn0,flag0; void sys_err(char* s…

http使用post上传文件时,请求头和主体信息总结

请求头必须配置如下行&#xff1a; Content-Type : multipart/form-data; boundary---12321 boundary---12321位文件的分界线 body如下&#xff1a; "-----12321\r\n" //分割文件时加-- "Content-Disposition: form-data; name\"…

Linu系统编程---9(SIGCHLD 信号,信号传参,中断系统调用)

SIGCHLD 信号 SIGCHLD 的产生条件 子进程终止时子进程接收到 SIGSTOP 信号停止时子进程处在停止态&#xff0c;接受到 SIGCONT 后唤醒时 借助 SIGCHLD 信号回收子进程 子进程结束运行&#xff0c;其父进程会收到 SIGCHLD 信号。该信号的默认处理动作是忽略。可以捕捉该信号…

Linu系统编程---10(Linux的终端,线路规程,网络终端,进程组)

终端 输入输出设备的总称 在 UNIX 系统中&#xff0c;用户通过终端登录系统后得到一个 Shell 进程&#xff0c;这个终端成为 Shell 进程的控制终端&#xff08;Controlling Terminal&#xff09;&#xff0c; 进程中&#xff0c;控制终端是保存在 PCB 中的信息&#xff0c;而 …

Linux系统编程---11(会话,守护进程,创建守护进程)

会话 创建会话 创建一个会话需要注意以下6点注意事项 调用进程不能是进程组组长&#xff0c;该进程变成新会话首进程该进程成为一个新进程组的组长进程需要root权限&#xff08;nbuntu不需要&#xff09;新会话丢弃原有的控制终端&#xff0c;该会话没有控制终端该调用进程是…

Linux系统编程----12(线程概念,Linux线程实现原理,栈中ebp指针和ebp指针,线程的优缺点和共享资源)

线程概念 什么是线程 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列” 一切进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行在Linux系统中&#xff0…

Linux系统编程---13(线程控制函数,创建线程,循环创建多个线程,线程间共享全局变量)

线程控制 操作系统并没有提供创建线程的系统调用接口&#xff0c;因此大佬们封装了一个线程的接口库实现线程控制。意为着用户创建线程都使用的是库函数&#xff08;所以有时候我们说创建的线程是一个用户态线程&#xff0c;但是在内核中对应有一个轻量级进程实现线程程序的调…

Linux系统编程---14(回收子线程,回收多个子线程,线程分离,杀死线程)

回收子线程 pthread_join 函数 阻塞等待线程退出&#xff0c;获取线程退出状态 其作用&#xff0c;对应进程中 waitpid() 函数。 int pthread_join (pthread_t thread,void** retval); 成功&#xff1a;0&#xff0c;失败&#xff1a;错误号 参数&#xff1a;thread&#x…

Linux系统编程----15(线程与进程函数之间的对比,线程属性及其函数,线程属性控制流程,线程使用注意事项,线程库)

对比 进程 线程 fork pthread_create exit (10) pthread_exit &#xff08;void *&#xff09; wait (int *) pthread_join &#xff08;&#xff0c;void **&#xff09;阻塞 kill pthread_cancel ();必须到取消点&#xff08;检查点&#xff09;&#xff1a;…

Linux系统编程----16(线程同步,互斥量 mutex,互斥锁的相关函数,死锁,读写锁)

同步概念 所谓同步&#xff0c;即同时起步&#xff0c;协调一致。不同的对象&#xff0c;对“同步”的理解方式略有不同。如&#xff0c;设备同步&#xff0c;是指在两 个设备之间规定一个共同的时间参考&#xff1b;数据库同步&#xff0c;是指让两个或多个数据库内容保持一致…

Linux系统编程---17(条件变量及其函数,生产者消费者条件变量模型,生产者与消费者模型(线程安全队列),条件变量优点,信号量及其主要函数,信号量与条件变量的区别,)

条件变量 条件变量本身不是锁&#xff01;但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。 主要应用函数&#xff1a; pthread_cond_init 函数pthread_cond_destroy 函数pthread_cond_wait 函数pthread_cond_timedwait 函数pthread_cond_signa…

Linux系统编程---18(线程池相关概念及其实现)

线程池 概念&#xff1a; 一堆线程任务队列 作用 避免大量线程频繁的创建/销毁时间成本避免瞬间大量线程创建耗尽资源&#xff0c;程序崩溃危险 实现 创建固定数量的线程创建一个线程安全的任务队列 一种线程使用模式。 线程过多会带来调度开销&#xff0c;进而影响缓…

设计模式--1(设计模式基础,设计模式基本原则,设计模式分类)

设计模式基础 模式 在一定环境中解决某一问题的方案&#xff0c;包括三个基本元素–问题&#xff0c;解决方案和环境。大白话&#xff1a;在一定环境下&#xff0c;用固定套路解决问题。 设计模式 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使…

设计模式----2(简单工厂模式的概念,简单工厂模式的实现,简单工厂模式的优缺点)

简单工厂模式 简单工厂模式的概念 简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式。通过专门定义一个类来负 责创建其他类的实例&#xff0c;被创建的实例通常都具有共同的父类。 具体分类 工厂&#xff08;Creator&#xff09;角色 简单工厂模式的核心&#xff0…

设计模式---3(工厂方法模式的概念,工厂方法模式的实现,工厂方法模式和简单工厂模式比较)

工厂方法模式 概念 工厂方法模式同样属于类的创建型模式又被称为多态工厂模式 。 工厂方法模式的意义 定义一个创建产品对象的工厂接口&#xff0c;将实际创建工作推迟到子类当中。 核心工厂类不再负责产品的创建&#xff0c;这样核心类成为一个抽象工厂角色&#xff0c;仅…