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

【Linux】进程间通信

linux运维服务器操作系统进程间通信 2023-10-26 09:10:09 866人浏览 薄情痞子
摘要

目录 1. 进程间通信 1.1. 进程间通信的目的 1.2. 如何实现进程间通信 2. 管道通信 2.1. 匿名管道 2.1.1 创建匿名管道 2.1.2 . 深入理解匿名管道 2.2. 命名管道 2.2.1. 创建命名管道 3. sys

目录

1. 进程间通信

1.1. 进程间通信的目的

1.2. 如何实现进程间通信

2. 管道通信

2.1. 匿名管道

2.1.1 创建匿名管道

2.1.2 . 深入理解匿名管道

2.2. 命名管道

2.2.1. 创建命名管道

3. system V 标准进程间通信

3.1. 共享内存

3.1.1. 实现原理

3.1.2. 代码实现

3.2. 消息队列(了解)

3.2.1 实现原理

3.3. 信号量(了解)

3.3.1. 实现原理


1. 进程间通信

1.1. 进程间通信的目的

进程之间可能会存在特定的协同工作的场景,而协同就必须要进行进程间通信,协同工作可能有以下场景。

  • 数据传输:一个进程需要将它的数据发送给另一个进程

  • 资源共享:多个进程之间共享同样的资源。

  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事件。

  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变

1.2. 如何实现进程间通信

由于一个进程是不能访问到另一个进程的资源的,即进程之前是具有独立性的。

那么进程之间要通信,就不能使用属于进程的资源,而应该使用一份公共的资源。

所以进程间通信的本质是:由OS参与,提供一份所以进程都能访问的公共资源。

而公共资源是什么,例如:文件、队列、内存块。

2. 管道通信

2.1. 匿名管道

适用场景:父子进程间通信。

基本原理:通过打开同一个文件,父子进程对文件进行读写操作,父子进程在文件内核缓冲区中写入或读出数据,从而实现通信。

2.1.1 创建匿名管道

使用接口

pipe:创建一个管道,参数为输出型参数,打开两个文件描述符(fd),返回值为0表示打开失败。

具体代码:

#include#include#include#includeint main(){    int pipefd[2] = {0};    if(pipe(pipefd) != 0)    {        perror("pipe");        return 1;    }    // 父进程读取数据,子进程写入数据    // 规定:pipedfd[0]为读取端,pipefd[1]为写入端    if(fork() == 0)    {        //child        close(pipefd[0]);// 关闭读取端        const char* msg = "hello world\n";        while(1)        {            write(pipefd[1], msg, strlen(msg));            sleep(1);        }        exit(0);    }    // father    close(pipefd[1]);// 关闭写入端    while(1)    {        char buffer[64] = {0};        ssize_t s = read(pipefd[0], buffer, sizeof(buffer));// 如果read的返回值是0,表示子进程关闭了文件描述符        if(s == 0)        {            printf("child quit\n");            break;        }        else if(s > 0)        {            buffer[s] = 0;// 子进程写入时没有添加'\0',需要手动添加            printf("child say:%s",buffer);        }        else        {            printf("read error\n");          break;        }    }    return 0;}

子进程写入数据,父进程读出数据,这样就实现了简单的父子进程间的通信:

问题分析:为什么上面的代码中,需保证读端比写端快?

因为管道是面向字节流的,字符串之间没由规矩分隔符,如果读取速度慢于写入速度,可能读端还没有将整个字符串读完,写端又写入了数据,会导致数据混乱。

 

2.1.2 . 深入理解匿名管道

匿名管道的五个特点:
  1. 只能单向通信的信道

  2. 面向字节流

  3. 只能在父子进程间通信

  4. 管道自带同步机制,原子性写入

  5. 管道也是文件,管道的生命周期随进程

#include#include#include#includeint main(){    int pipefd[2] = {0};    if(pipe(pipefd) != 0)    {        perror("pipe");        return 1;    }        if(fork() == 0)    {        //child        close(pipefd[0]);// 关闭读取端       int count = 0;       char c = 'a';        while(1)        {            write(pipefd[1], &c, 1);            count++;            printf("%d\n", count);        }        exit(0);    }    // father    close(pipefd[1]);// 关闭写入端    while(1)    {          sleep(5);          char buffer[4*1024+1] = {0};          ssize_t s = read(pipefd[0], buffer, sizeof(buffer)-1);          buffer[s] = 0;          printf("father take:%s\n", buffer);    }    return 0;}

云服务器中,管道的大小为64KB,写端写满后不会再写,会等读端读取管道内容,且读取4KB后才会重新写入(读端的容量为4KB)。

管道读写规则

当没有数据可读时

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,进而可能导致write进程退出

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

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

即匿名管道的四种情况:

  1. 读端不读或读的慢,写端要等读端

  2. 读端关闭,写端收到SIGPIPE信号直接终止

  3. 写端不写或者写的慢,读端要等写端

  4. 写端关闭,读端会读完管道内的数据然后再读,会读到0,表示读道文件结尾

2.2. 命名管道

为了解决匿名管道只能在父子进程间通信的缺陷,引入了命名管道。

其性质除了能让任意进程间通信外,与匿名管道基本一致,即创建一个文件一个进程往文件中写数据,一个进程读数据,且不让文件内容刷新到磁盘上,从而实现任意进程间的通信。

2.2.1. 创建命名管道

命令行创建

使用命令 mkfifo 管道

然后使用一个简单的shell脚本,将 hello world 每间隔一秒输入到管道中,然后另一边读取管道中的内容。

通过这种方式,显示不是重点。

代码创建

使用接口:mkfifo 

因为是不同进程间的通信,所以这里需要创建两个进程:

comm.h

#include#include#include#include#include#include​#define MY_FIFO "./fifo"
server.c
#include"comm.h"int main(){ umask(0); if(mkfifo(MY_FIFO, 0666) < 0) //创建命名管道 {     perror("mkfifo");     return 1; }​ // 只需要文件操作即可 int fd = open(MY_FIFO, O_RDONLY); if(fd < 0) {     perror("open");     return 2; }​ // 业务逻辑 while(1) {     char buffer[64] = {0};     ssize_t s = read(fd, buffer, sizeof(buffer)-1);     if(s > 0)     {         buffer[s] = 0;         printf("client-> %s\n", buffer);     }     else if(s == 0)     {         printf("client quit...\n");         break;     }     else     {         perror("read");         break;     } }​ close(fd); return 0;}

client.c

#include"comm.h"​​int main(){ int fd = open(MY_FIFO, O_WRONLY); if(fd < 0) {     perror("open");     return 1; }​ // 业务逻辑 while(1) {     printf("请输入-> ");     fflush(stdout);     char buffer[64] = {0};     ssize_t s = read(0, buffer, sizeof(buffer)-1); // 从显示器上读取数据,然后写入到文件中     if(s > 0)     {         buffer[s-1] = 0;         printf("%s\n", buffer);         write(fd, buffer, strlen(buffer));     } } return 0;}

运行起来后,就实现了简单的命名管道的通信:

为什么命名管道有名字,而匿名管道没有?

命名管道有名字是为了保证让不同进程看到同一个文件。

匿名管道没有名字,是因为他是通过父子继承放入方式,看到同一份资源不需要名字。

3. system V 标准进程间通信

system V:同一主机内的进程间通信方案,在OS层面专门为进程间通信设计的方案

进程间通信的本质:让不同的进程看到同一份资源

system V标准下的三种通信方式

  1. 共享内存

  2. 消息队列

  3. 信号量

3.1. 共享内存

3.1.1. 实现原理

​​​​​​​​​​​​
  1. 通过系统调用,在内存中创建一份内存空间

  2. 通过系统调用,让进程"挂接"到这份新开辟的内存空间上(即在页表上建立虚拟地址与物理地址的映射关系)

  3. 去关联(挂接)

  4. 释放共享内存

使用接口:

shmget:申请共享内存

#include #include int shmget(key_t key, size_t size, int shmflg);// key:创建共享内存时的算法数据结构中唯一标识符,由用户自己设定需用到接口ftok// size:共享内存的大小,建议是4KB的整数倍// shmflg:有两个选项:IPC_CREAT(0),创建一个共享内存,如果已经存在则返回共享内存;IPC_EXCL(单独使用没有意义)// IPC_CREAT|IPC_EXCL(如果调用成功,一定会得到一个全新的共享内存):如果不存在共享内存,就创建;反之,返回出错// 返回值:shmdi,描述共享内存的标识符​#include #include key_t ftok(const char *pathname, int proj_id); // 算法生成key// pathname:自定义路径名// proj_id:自定义项目id

shmctl:控制共享内存

#include #include int shmctl(int shmid, int cmd, struct shmid_ds *buf);// shmid:共享内存id// cmd:控制方式,这里我们只使用IPC_RMID 选项,表示删除共享内存// buf:描述共享内存的数据结构

struct_shmid_ds结构体:

shmat、shmdt:关联、去关联共享内存

#include #include void *shmat(int shmid, const void *shmaddr, int shmflg); // 关联// shmid:共享内存id// shmaddr:挂接地址(自己不知道地址,所以默认为NULL)// shmflg:挂接方式,默认为0// 返回值:挂接成功返回共享内存起始地址(虚拟地址),类似C语言malloc​    int shmdt(const void *shmaddr); // 去关联(取消当前进程和共享内存的关系)// shmaddr:去关联内存地址,即shmat返回值// 返回值:调用成功返回0,失败返回-1

命令行查看共享内存

ipcs -m // ipcs 查看ipc资源

system V 的IPC资源,生命周期随内核,只能通过程序员显示释放(或者OS重启)

命令行删除共享内存方法:

ipcrm -m shmid

3.1.2. 代码实现

comm.h

#include#include#include#include#include#include​#define PATH_NAME "./"#define PROJ_ID 0x6666#define SIZE 4096

server.c

#include"comm.h"​int main(){ key_t key = ftok(PATH_NAME, PROJ_ID); if(key < 0) {     perror("ftok");     return 1; } printf("key-> %x\n", key);​ int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666); // 创建全新共享内存 if(shmid < 0) {     perror("shmget");     return 1; } printf("shmid-> %d\n", shmid);​ char* mem = (char*)shmat(shmid, NULL, 0); // 通信逻辑 while(1) {     printf("%s\n", mem);// 打印mem内存中的内容     sleep(1);  }​ shmdt(mem);​ shmctl(shmid, IPC_RMID, NULL);​ return 0;}

client.c

#include"comm.h"​int main(){ key_t key = ftok(PATH_NAME, PROJ_ID); if(key < 0) {     perror("ftok");     return 1; }​ int shmid = shmget(key, SIZE, IPC_CREAT); if(shmid < 0) {     perror("shmget");     return 1; }​ // 挂接 char* mem = (char*)shmat(shmid, NULL, 0); // 通信逻辑 char c = 'A'; while(c <= 'Z') {     mem[c - 'A'] = c;     c++;     mem[c - 'A'] = 0;     sleep(2); }​ // 去关联 shmdt(mem);​ //该共享内存不由client创建,所以不用它删除​ return 0;}

运行结果:

使用共享内存进行通信时,不需要使用read和write 接口。

共享内存是所有进程间通信中速度最快的。

共享内存不提供任何同步或互斥机制,需要程序员自行保证数据安全

ps: 共享内存在内核中的申请的基本单位是页,内存页的大小为4KB,如果申请4097个字节,内核会分配8KB空间。

3.2. 消息队列(了解)

3.2.1 实现原理

接口类似与共享内存,底层是一个队列结构

msgget:创建消息队列

#include #include #include int msgget(key_t key, int msgflg);

msGCtl:控制消息队列

#include #include #include int msgctl(int msqid, int cmd, struct msqid_ds *buf);

3.3. 信号量(了解)

什么是信号量?

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

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

临界资源:同时被多个进程访问的资源。例如:显示器打印,共享内存,消息队列

临界区:用来访问临界资源的代码,就是临界区。

原子性:执行事件时没有中间过程,且操作不可中断,要么执行完,要么没有执行。

互斥:在任意时刻,只允许一个进程进入临界资源。

同步:两个或多个数据库、文件、模块、线程之间用来保持数据内容一致性的机制。

3.3.1. 实现原理

接口类似共享内存

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, ...);

所有的ipc资源都是通过数组组织起来的。

来源地址:https://blog.csdn.net/Edward_Asia/article/details/124544624

--结束END--

本文标题: 【Linux】进程间通信

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

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

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

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

下载Word文档
猜你喜欢
  • 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】进程间通信——管道
    文章目录 进程间通信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
  • Linux常见的进程间通信
    目录 管道pipe匿名管道接口介绍示例代码 fifo命名管道接口介绍代码示例匿名管道与命名管道的区别 shm共享内存接口介绍相关指令代码示例特点总结 信号信号量socket...
    99+
    2023-09-05
    linux 服务器 运维
  • Linux进程间通信的方式
    这篇文章主要介绍“Linux进程间通信的方式”,在日常操作中,相信很多人在Linux进程间通信的方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Linux进程间通信的方式”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-16
  • Linux进程间通信怎么实现
    这篇文章主要讲解了“Linux进程间通信怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux进程间通信怎么实现”吧!共享内存共享内存可以说是最有用的进程间通信方式,也是最快的IP...
    99+
    2023-07-05
  • 进程间的通信
      使用 multiprocessing 里的 Queue() import multiprocessing def download_from_web(q): """下载数据""" # 模拟从网上下载的数据 ...
    99+
    2023-01-30
    进程 通信
  • python 进程间通信
    python multiprocessing multiprocessing 在2.6才开始使用 multiprocessing 是一个使用方法类似threading模块的进程模块。允许程序员做并行开发。并且可以在UNIX和Windo...
    99+
    2023-01-31
    进程 通信 python
  • Linux进程间通信机制有哪些
    Linux中的进程间通信机制有:1.管道,可用于具有亲缘关系进程间的通信;2.信号,用于通知接受进程有某种事件发生;3.消息队列,消息的链接表;4.共享内存,多个进程访问同一块内存空间;Linux中的进程间通信机制有以下几种管道管道是一种可...
    99+
    2024-04-02
  • 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
  • Node.js中的进程间通信
    目录前置知识文件描述符文件描述符的重定向shell 对文件描述符的重定向c函数对文件描述符的重定向dupdup2Node中通信原理unix domain socket是什么如何实现流...
    99+
    2024-04-02
  • golang进程间怎么通信
    在Go语言中,进程间通信可以使用以下几种方式: 1.管道(Pipe):通过Pipe可以在父子进程之间实现单向通信。在Go语言中,可以...
    99+
    2023-10-20
    golang
  • Python进程间通信方式
    目录一、通信方式二、Queue介绍三、方法介绍三、生产者和消费者模型四、什么是生产者消费者模式实现方式一:Queue实现方式二:利用JoinableQueue一、通信方式 进程彼此之...
    99+
    2024-04-02
  • 什么是进程间通信
    这篇文章主要介绍“什么是进程间通信”,在日常操作中,相信很多人在什么是进程间通信问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是进程间通信”的疑惑有所帮助!接下来,请跟着...
    99+
    2024-04-02
  • Linux进程间socketpair通信被阻塞的问题
    在Linux系统中,使用socketpair函数创建的套接字对可以用于进程间通信。但是,在进行通信时可能会遇到阻塞的问题。一种常见的...
    99+
    2023-09-08
    Linux
  • linux进程间的通信方式有哪几种
    这篇文章主要介绍“linux进程间的通信方式有哪几种”,在日常操作中,相信很多人在linux进程间的通信方式有哪几种问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”linux进程间的通信方式有哪几种”的疑惑有所...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作