iis服务器助手广告广告
返回顶部
首页 > 资讯 > 操作系统 >Linux常见的进程间通信
  • 399
分享到

Linux常见的进程间通信

linux服务器运维 2023-09-05 11:09:44 399人浏览 八月长安
摘要

目录 管道pipe匿名管道接口介绍示例代码 fifo命名管道接口介绍代码示例匿名管道与命名管道的区别 shm共享内存接口介绍相关指令代码示例特点总结 信号信号量socket

目录

管道

在这里插入图片描述

管道是一种较老的,半双工通信方式,即数据只能向一个方向流动(即一个进程进行写操作,一个进程进行读操作);

在这里插入图片描述
如果要进行双向通信,则需要建立起两个管道。
在这里插入图片描述

管道分为两种,匿名管道命名管道;

pipe匿名管道

匿名管道就是具有血缘关系的进程进行通信,常见于父子进程之间。

父子进程创建匿名管道(半双工)的过程如下:

在这里插入图片描述

可以看到,匿名管道通信的原理就是某个父进程在他的fd文件描述符数组中维护了匿名管道文件,子进程继承之后双方用于通信;

匿名管道文件的实质和标准IO类似,是在内核中的一片特定缓冲区; 因此数据交互的时候,涉及到用户态和内核态之间的数据拷贝,效率不高的;

(后面要讲的共享内存shm是直接映射到共享内存区,不需要进行拷贝,高效)

接口介绍

#include 功能:创建一匿名管道原型int pipe(int fd[2]);参数fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端返回值:成功返回0,失败返回错误代码

示例代码

#include #include #include int main(){    int pipe_fds[2];    int ret = pipe(pipe_fds); // 父亲创建管道,0为读端,1为写端    if (ret == -1)    {        perror("pipe");        return 1;    }    int pid = fork(); // 创建子进程    if (pid < 0)    {        // error        perror("fork");        return 2;    }    else if (pid == 0)    {        // child        close(pipe_fds[1]); // 子进程关闭写端        char buf[128];        // 子进程从管道中读取数据        read(pipe_fds[0], buf, sizeof(buf) - 1);        printf("%s\n", buf);        close(pipe_fds[0]);    }    else    {        // father        close(pipe_fds[0]); // 父进程关闭读端        // 父进程往管道内写数据        const char *msg = "I am father.\n";        write(pipe_fds[1], msg, strlen(msg));        close(pipe_fds[1]);    }    return 0;}

运行结果

在这里插入图片描述

fifo命名管道

FIFO,也叫做命名管道,它是一种文件类型。

  1. FIFO可以在无关的进程之间交换数据,与匿名管道不同;
  2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。(这个文件仅用于双方通信)

接口介绍

#include #include //创建一个命名管道int mkfifo(const char *pathname, mode_t mode);参数:第一个参数为这个特殊文件的路径;第二个参数mode 与 open 函数中的 mode 相同,设置标志位。  当 open 一个 FIFO 时,这个FIFO是否设置非阻塞标志(O_NONBLOCK)的区别:1.若没设置 O_NONBLOCK(默认),只读的一方 open打开 这个FIFO 要 阻塞到 某个其他进程为写 而打开它                      类似的,只写的一方 open打开 这个FIFO 要 阻塞到 某个其他进程为读 而打开它2.若设置了 O_NONBLOCK,则只读 open打开时会立即返回。(执行下面的代码,如果有人向这个fd写了,那么这边就能拿到,不用阻塞等待处理)                       只写 open 打开时,如果没有进程已经为读而打开该将出错返回 -1 其 errno 置 ENXio,否则打开成功,直接就可以写入数据了;

下面是借助fifo通信的模型:

在这里插入图片描述

结论:

数据 还是存储在内核的缓冲区当中的(fifo创建的管道文件只是一个特殊文件,不存东西的文件,底层机制和匿名管道一样的 都是拿内核缓冲区做中介

管道文件的作用是为了让不同的进程可以找到这块缓冲区 (这点匿名管道做不到)

代码示例

write.c

#include #include #include #include #include #include #include //       int mkfifo(const char *pathname, mode_t mode); int main(){     if(mkfifo("myfifo",0600) == -1 && errno != EEXIST)//创建命名管道        {                printf("mkfifo failed\n");                perror("why");        }            int nread;        char buf[30] = "message from myfifo";                 int fd = open("./myfifo",O_WRONLY);//以 只写 的方式打开管道,程序阻塞在这,直到其他进程为读而打开它        if(fd < 0)        {                printf("write open failed\n");        }        else        {                printf("write open success\n");        }         while(1)//不断的通过管道(open的fd)给read端发送数据发送        {                sleep(1);                write(fd,buf,strlen(buf));        }        close(fd);         return 0;}

先运行write端,则命名管道就会创建好,然后等待着read端的到来,进行管道通信;

read.c

#include #include #include #include #include #include  int main(){        int nread;        char buf[30] = {'\0'};          int fd = open("./myfifo",O_RDONLY);//以 只读的形式打开管道,程序阻塞在这,直到有另一个进程对其执行写操作        if(fd < 0)        {                printf("read open failed\n");        }else        {                printf("read open successn\n");        }         while(1)//反复收取数据并打印出来        {                nread = read(fd,buf,sizeof(buf));                printf("read %d byte,context is:%s\n",nread,buf);        }         close(fd);         return 0;}

运行结果:

在这里插入图片描述

可以看到,当write端创建命名管道之后不断通过管道给read端发送数据,read端通过命名管道收到数据并打印出来;

匿名管道与命名管道的区别

  • 匿名管道由pipe函数创建并打开 (维护在进程双方的task_struct的fd数组中) ; 命名管道由mkfifo函数创建,打开用open(以特殊文件的形式维护)
  • 命名管道可以在不同的进程之间共享,匿名管道只能在创建它的进程内使用。
  • 命名管道可以在系统中长期存在,直到被显式删除,匿名管道在使用完毕进程退出后会自动销毁。

其底层都是系统维护了一块缓冲区;他们都是半双工;

shm共享内存

在这里插入图片描述

共享内存根据其名字就可以推测与内存中的共享区有关。实际上,共享内存的使用要比管道的简单

在这里插入图片描述

接口介绍

1.key_t ftok(const char *pathname, int proj_id);功能:用来生成System V IPC密钥,key是用来唯一标识共享内存块的值 file to key参数pathname:共享内存文件的给定路径名proj_id:project id这两个参数可以随意设置,只不过要保证使用共享内存的进程这两个参数设置需一样。返回值:成功返回生成的key值,失败返回-1        2. int shmget(key_t key, size_t size, int shmflg); //get创建功能:用来创建共享内存句柄参数 key:这个共享内存段名字 size:共享内存大小 shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的     返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1          3. void *shmat(int shmid, const void *shmaddr, int shmflg);//attach 链接功能:将共享内存段连接到进程地址空间参数 shmid: 共享内存标识码 shmaddr:指定连接的地址 shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY返回值:成功返回一个指针,指向共享内存第一个字节;失败返回-1     4. int shmdt(const void *shmaddr);//detach 脱离功能:将共享内存段与当前进程脱离参数 shmaddr: 由shmat所返回的指针返回值:成功返回0;失败返回-1注意:将共享内存段与当前进程脱离 不等于 删除共享内存段     5. int shmctl(int shmid, int cmd, struct shmid_ds *buf);//control 控制功能:用于控制实际的共享内存参数 shmid:由shmget返回的共享内存标识码 cmd:将要采取的动作(有三个可取值,如下表) buf:指向一个保存着共享内存的模式状态和访问权限的数据结构返回值:成功返回0;失败返回-1

shmctl中cmd的几种命令:

在这里插入图片描述

对于共享内存的key和shmid,我们可以类比文件中的inode与fd的关系。
虽然文件系统一inode唯一标识文件,但在实际使用中仍是以fd文件描述符去操作文件。\

相关指令

ipcs -m:查看当前共享内存的信息  //ipc == (Inter-Process Communication,进程间通信)ipcrm -m shmid:删除对应shmid的共享内存块

在这里插入图片描述

代码示例

server进程创建共享内存,获取key值及shmid;

client进程通过shmid去挂接共享内存,然后观察两个进程通过共享内存进行通信的现象:

server.c

#define _SVID_SOURCE 1#include #include #include #include #include //创建key时的两个参数,s c需要统一#define PATH_NAME "/home/lyl/2022-3-20"#define PROJ_ID 0x6666#define SIZE 4097int main(){  //获取key值  key_t key = ftok(PATH_NAME, PROJ_ID);  if(key == -1)  {    perror("ftok");    return 1;  }  printf("key: %x\n", key);  //获取shmid  int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0644);//若不存在则创建共享内存,若存在则报错  if(shmid == -1)  {    perror("shmget");    return 2;  }  printf("shmid:%d\n",shmid);  //让进程挂接共享内存,形成关联  char* addr = (char*)shmat(shmid, NULL, 0);   printf("server attached on shared memory\n");  if(addr == (char*)-1)  {    perror("shmat");    return 3;  }  printf("addr:%p\n", addr);  //从共享内存首地址读数据 并打印  while(1)  {    printf("%s\n", addr);    sleep(1);  }  //解除关联  shmdt(addr);  printf("server attached off shared memory\n");  shmctl(shmid, IPC_RMID, NULL);  printf("server deleted shared memory\n");  return 0;}

client.c

#define _SVID_SOURCE 1#include #include #include #include #include #include //创建key时的两个参数,s c需要统一#define PATH_NAME "/home/lyl/2022-3-20"#define PROJ_ID 0x6666#define SIZE 4097int main(){  //获取key值  key_t key = ftok(PATH_NAME, PROJ_ID);  if(key == -1)  {    perror("ftok");    return 1;  }  //获取shmid  int shmid = shmget(key, SIZE, IPC_CREAT);//不需要自己创建共享内存,server创建好了,直接获取shmid即可  if(shmid == -1)  {    perror("shmget");    return 2;  }  //让进程挂接共享内存,形成关联  char* addr = (char*)shmat(shmid, NULL, 0);   printf("client attached on shared memory\n");   //TODO    const char* msg = "I am process client\n";//等会用于通信的数据  //逐字符向共享内存写数据  for(size_t i = 0; i < strlen(msg); i++)  {    addr[i] = *(msg + i);    sleep(1);  }  //接触关联  shmdt(addr);  printf("client attached off shared memory\n");  return 0;}

client进程向共享内存中不断写入数据,然后server进程从共享内存中读取数据并打印。

在这里插入图片描述

通过分析,共享内存区别于管道借助阻塞式read和write进行通信而是直接对同一块内存进行操作

因此共享内存通信的两个进程独立,不像管道会存在阻塞现象。

而且使用之后的共享内存中数据不会自动清除,下次使用还能拿到上次通信的数据,因此每次进程结束后都需要主动释放共享内存,否则再次执行进程时会报错;

特点总结

  • 共享内存不存在同步与互斥机制,使用的进程相互独立。因此对共享内存的操作是非进程安全的
  • 共享内存只有在当前映射链接数为0时,才能被被真正删除
  • 共享内存是以内存为临界资源进行通信,不需要 内核态与用户态之间的 拷贝数据,因此时进程通信中最快的形式(但是需要我们自己保证同步与互斥)
  • 共享内存的生命周期随内核,只要不主动删除,其就会随内核一直存在,除非重启系统。

信号

在这里插入图片描述

信号一般用于一些异常情况下的进程间通信,是一种异步通信,它的数据结构一般就是一个数字。

linux操作系统中,为了响应各种各样的事件,提供了几十种信号,分别代表不同的意义。我们可以通过kill -l命令,查看所有的信号。

运行在shell终端的进程,我们可以通过键盘输入某些组合键的时候,给进程发送信号。例如

Ctrl+C产生 SIGINT 信号,表示终止该进程;
Ctrl+Z产生 SIGTSTP 信号,表示停止该进程,但还未结束;
如果进程在后台运行,可以通过kill命令的方式给进程发送信号,但前提需要知道运行中的进程PID号,例如:

kill -9 1050,表示给PID为1050的进程发送SIGKILL 信号,用来立即结束该进程(例如:win下在任务管理器右键结束进程);

所以,信号事件的来源主要有硬件来源(如键盘Cltr+C)软件来源(如kill命令)

信号是进程间通信机制中唯一的异步通信机制

进程需要为信号设置相应的监听处理,当监听到特定信号时,接着执行相应的操作,类似很多编程语言里的通知机制。

关于信号的更多理解和操作,可以移步这篇博客

信号量

在这里插入图片描述

信号量不是以传输数据为目的,通过共享“资源”的方式,来达到多个进程的同步和互斥的目的!

本质是一个计数器,衡量临界资源中的资源数目。

临界资源:同时被多个进程访问的资源。例如:显示器打印,共享内存,消息队列
临界区:用来访问临界资源的代码,就是临界区。
原子性:执行事件时没有中间过程,且操作不可中断,要么执行完,要么没有执行。
互斥:在任意时刻,只允许一个进程进入临界资源。
同步:两个或多个数据库、文件、模块、线程之间用来保持数据内容一致性的机制。

实现原理(类似shm共享内存)

  • semget:创建信号量
#include #include #include int semget(key_t key, int nsems, int semflg);
  • semctl:控制信号量
#include #include #include int semctl(int semid, int semnum, int cmd, ...);

关于信号量的使用,线程间通信(为了同步)的文章可以参考一下参考本人文章,将其与本文解耦的原因是,信号量作为同步的一种重要机制,并且是保证临界区资源正确被访问的重要手段(通过计数器方式),需要系统理解和学习;

同时,信号量自身也是临界资源,它内部的PV原语保证了他的操作原子性;

Socket套接字

在这里插入图片描述

关于进程间socket套接字的通信原理,移步本人这篇文章;

将其与本文解耦的原因是,socket是一个庞大的学习内容,除了能本地进程间通信之外,也能跨网络进程间通信,并且是学习tcp,UDP协议的重要知识点;需要系统理解和学习

来源地址:https://blog.csdn.net/wtl666_6/article/details/128768113

--结束END--

本文标题: Linux常见的进程间通信

本文链接: https://www.lsjlt.com/news/395320.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Linux常见的进程间通信
    目录 管道pipe匿名管道接口介绍示例代码 fifo命名管道接口介绍代码示例匿名管道与命名管道的区别 shm共享内存接口介绍相关指令代码示例特点总结 信号信号量socket...
    99+
    2023-09-05
    linux 服务器 运维
  • Linux-进程间通信
    进程间通信 进程间通信介绍进程间通信目的进程间通信发展进程间通信分类 管道匿名管道匿名管道特点匿名管道读写规则 命名管道创建一个命名管道命名管道的打开规则用命名管道实现server&client通信 system...
    99+
    2023-08-19
    linux 服务器 网络 c++ C语言
  • 【Linux】进程间通信
    目录 1. 进程间通信 1.1. 进程间通信的目的 1.2. 如何实现进程间通信 2. 管道通信 2.1. 匿名管道 2.1.1 创建匿名管道 2.1.2 . 深入理解匿名管道 2.2. 命名管道 2.2.1. 创建命名管道 3. sys...
    99+
    2023-10-26
    linux 运维 服务器 操作系统 进程间通信
  • 常见的Linux进程通信方法是什么
    这篇文章主要为大家分析了常见的Linux进程通信方法是什么的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“常见的Linux进程通信方法是什么”的知识吧。进程虽...
    99+
    2023-06-28
  • Linux进程间通信的方式
    这篇文章主要介绍“Linux进程间通信的方式”,在日常操作中,相信很多人在Linux进程间通信的方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Linux进程间通信的方式”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-16
  • 【Linux】进程间通信——管道
    文章目录 进程间通信1.1进程间通信介绍1.2进程间通信目的1.3进程间通信分类 管道2.1管道介绍2.2匿名管道pipe读写特征管道特征 2.3命名管道mkfifo创建管道文件删除管道文件通信 总结...
    99+
    2023-08-24
    linux 网络 运维 服务器
  • Linux进程间如何通信
    这篇文章主要介绍“Linux进程间如何通信”,在日常操作中,相信很多人在Linux进程间如何通信问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Linux进程间如何通信”的疑惑...
    99+
    2024-04-02
  • 进程间的通信
      使用 multiprocessing 里的 Queue() import multiprocessing def download_from_web(q): """下载数据""" # 模拟从网上下载的数据 ...
    99+
    2023-01-30
    进程 通信
  • Linux进程间通信怎么实现
    这篇文章主要讲解了“Linux进程间通信怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux进程间通信怎么实现”吧!共享内存共享内存可以说是最有用的进程间通信方式,也是最快的IP...
    99+
    2023-07-05
  • python 进程间通信
    python multiprocessing multiprocessing 在2.6才开始使用 multiprocessing 是一个使用方法类似threading模块的进程模块。允许程序员做并行开发。并且可以在UNIX和Windo...
    99+
    2023-01-31
    进程 通信 python
  • Linux操作系统 进程之间的通信
     进程之间的通信预备知识:用户态和内核态,当一个进程在执行用户自己的代码时处于用户运行态(用户态);当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态)。进程之间的通信(Inter Processs Communic...
    99+
    2023-06-05
  • Linux进程间通信的方式是什么
    本篇内容主要讲解“Linux进程间通信的方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux进程间通信的方式是什么”吧!·进程间通信:操作系统为系统提供的用于实现进程间通信的方式进...
    99+
    2023-06-29
  • Linux进程间的通信方式有哪些
    本篇内容主要讲解“Linux进程间的通信方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux进程间的通信方式有哪些”吧!进程的概念进程是操作系统的概念,每当我们执行一个程序时,对于...
    99+
    2023-06-16
  • Linux进程间通信的方式有哪些
    本文小编为大家详细介绍“Linux进程间通信的方式有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“Linux进程间通信的方式有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.管道管道分为有名管道和无名...
    99+
    2023-06-28
  • Linux进程间通信机制有哪些
    Linux中的进程间通信机制有:1.管道,可用于具有亲缘关系进程间的通信;2.信号,用于通知接受进程有某种事件发生;3.消息队列,消息的链接表;4.共享内存,多个进程访问同一块内存空间;Linux中的进程间通信机制有以下几种管道管道是一种可...
    99+
    2024-04-02
  • Node.js中的进程间通信
    目录前置知识文件描述符文件描述符的重定向shell 对文件描述符的重定向c函数对文件描述符的重定向dupdup2Node中通信原理unix domain socket是什么如何实现流...
    99+
    2024-04-02
  • android实现线程间通信的四种常见方式
    1,通过Handler机制 主线程中定义Handler,子线程发消息,通知Handler完成UI更新,Handler对象必须定义在主线程中,如果是多个类直接互相调用,就不是很方便,...
    99+
    2024-04-02
  • Linux进程间socketpair通信被阻塞的问题
    在Linux系统中,使用socketpair函数创建的套接字对可以用于进程间通信。但是,在进行通信时可能会遇到阻塞的问题。一种常见的...
    99+
    2023-09-08
    Linux
  • linux进程间的通信方式有哪几种
    这篇文章主要介绍“linux进程间的通信方式有哪几种”,在日常操作中,相信很多人在linux进程间的通信方式有哪几种问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”linux进程间的通信方式有哪几种”的疑惑有所...
    99+
    2023-06-20
  • Python的进程间通信详解
    目录进程概述队列简介多进程队列的使用使用队列在进程间通信总结进程概述 ​ 进程(Process)是计算机中已运行程序的实体。进程与程序不同,程序本身只是指令、数据及器组织形式的描述,...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作