首页 > 系统相关 >Linux——进程间通信

Linux——进程间通信

时间:2024-07-29 16:57:35浏览次数:11  
标签:int 间通信 管道 fd Linux 进程 共享内存 buff

一、进程间通信介绍

目的

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

发展

  • 管道
  • System V进程通信
  • POSIS进程间通信

分类

管道

  • 匿名管道
  • 命名管道

System V IPV

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

二、管道

  • 管道是最古老的进程间通信的形式。
  • 我们把从一个进程连接另一个进程是一个数据流称为一个"管道"

2.1 匿名管道

匿名管道:创建一种无名的管道,提供具有血缘关系的进程进行通信。

#include<unistd.h>
int pipe(int fd[2])
参数:fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返回错误代码
int main()
{
    int fd[2];
    // 创建匿名管道
    if (pipe(fd) < 0)
    {
        perror("pipe!");
        return 1;
    }
    // 创建子进程
    int id = fork();
    if (id == 0)
    {
        // child
        close(fd[0]);
        const char *mag = "hello linux\n";
        int count = 5;
        while (count)
        {
            write(fd[1], mag, strlen(mag));
            count--;
            sleep(1);
        }
        exit(0);
    }
    // father
    close(fd[1]);
    char buff[64];
    while (1)
    {
        ssize_t s = read(fd[0], buff, sizeof(buff));
        if (s > 0)
        {
            buff[s] = '\0';
            printf("child --->father#%s", buff);
            sleep(1);
        }
        else if (s == 0)
        {
            printf("read end\n");
            break;
        }
        else
        {
            printf("error\n");
            break;
        }
    }
    // 等待子进程结束,回收资源,防止僵尸进程。
    waitpid(id, NULL, 0);
    close(fd[0]);
    return 0;
}

<1> 如果说通信可以通过文件来作为媒介,那么为什么不直接open一个文件来呢?要用pipe来创建管道?

答:pipe创建的文件是内存文件,数据一定不会刷新到磁盘。并且用普通文件会有很多问题(同步与互斥),有IO参与会降低效率,没有必要。

<2> 创建子进程进行写入难道不会发生写时拷贝吗?

答:不会,管道是OS提供的,子进程写入时,不会改变父进程的数据区,故不会发生写时拷贝。

<3> 子进程还没写完,父进程就不会读取吗?

答:管道是自带同步与互斥的。不会发生子进程还没写完,父进程就开始读了。

管道读写规则

当没有数据可读时:

  • 子进程不write,父进程会被挂起。

当管道满了时:

  • 子进程会被挂起,等待父进程read

如果管道写端对应的文件描述符被关闭,read返回0
如果父进程write关闭时,子进程read没有意义,子进程会接收到13号信号退出。

用代码演示子进程接收信号退出:

int main()
{
    int fd[2];
    if (pipe(fd) < 0)
    {
        perror("pipe!");
        return 1;
    }
    pid_t id = fork();
    if (id == 0)
    {
        // child
        close(fd[0]);
        const char *mag = "hello linux\n";
        write(fd[1], mag, strlen(mag));
        exit(0);
    }
    // father
    // 关闭读端和写端
    close(fd[1]);
    close(fd[0]);
    int status = 0;
    waitpid(id, &status, 0);
    // 打印信号
    printf("child get signal:%d\n", status & 0x7F);
    return 0;
}

接收的是13号信号,信号是由OS发送的。

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

管道特点

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

2.2 命名管道

  • 匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件

创建一个命名管道

mkfifo filename

利用命名管道让两个毫不相干的进程进行通信

//server.c ->写

int main()
{
    int fd = open("filename", O_WRONLY);
    char *mag = "ni hao a\n";
    write(fd, mag, strlen(mag));
    close(fd);
    return 0;
}


//client.c ->读

int main()
{
    char buff[64];
    int fd = open("filename", O_RDONLY);
    ssize_t s = read(fd, buff, sizeof(buff));
    buff[s] = '\0';
    printf("srever----->client:%s", buff);
    close(fd);
    return 0;
}

命名管道也可以从程序里创建,相关函数有:

int mkfifo(const char *filename,mode_t mode);
第一个参数是文件名或者路径。第二个参数是文件的权限。
成功是返回0,失败是返回-1。

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

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
  • 命名管道作用于毫不相干的进程,匿名管道作用于具有血缘关系的进程

三、system V共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

共享内存示意图

共享内存函数

shmget函数

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

shmat函数

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

【说明】

  • shmaddr为NULL,内核会自动选择一个地址

  • shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。

  • shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)

  • shmflg=SHM_RDONLY,表示连接操作用来只读共享内存。

shmdt函数

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

shmctl函数

功能:用于控制共享内存
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数:shmid:key的标识符
	 cmd:将要采取的动作
	 buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0,失败返回-1。

标签:int,间通信,管道,fd,Linux,进程,共享内存,buff
From: https://blog.csdn.net/m0_73243771/article/details/140699294

相关文章

  • Linux操作系统下编译、链接过程详解
    gcc和g++的区别:gcc和g++是GNU编译器集合中的两个不同的编译器,它们之间的主要区别在于它们所针对的编程语言以及它们的行为和功能。1.编译器的目标语言:gcc是用于编译C语言的编译器,而g++是用于编译C++语言的编译器。因此它们分别用于编译不同的源代码文件;2.语法支持:gcc和......
  • linux shell read 按列读取txt文本
    前言全局说明一、说明通常情况下,如果文本里有多列数据,会先读入,然后用grep和awk先拆分成行,在拆分成列。这样费时费力,遇到特殊字符行,还不好处理。在解决别的问题时候,无意发现read有直接按列读取的功能。二、文件2.1存放两列数据的文件文件名:list.txt1libCommonA......
  • Linux shell mktemp -d命令生成临时文件
    前言全局说明一、说明二、mktemp命令2.1创建临时文件mktemp2.1创建临时目录mktemp-d三、命令行示例mktempll/tmp/tmp.fvi5gFbDgr四、sh脚本使用4.1创建tmpfile=$(mktemp)4.2删除rm"$tmpfile"免责声明:本号所涉及内容仅供安全研究与教学使用......
  • linux科研武器库 - 文件数量统计 - ls -l | grep "^-" | wc -l
    使用场景:文件数量统计,在科研场景中,更多是用于检验、核对数据集的样本数量,防止数据遗漏等意外情况。常用命令:ls-l|grep"^-"|wc-l作用:统计当前目录下,文件的个数(不包括目录/文件夹)ls-lR|grep"^-"|wc-l作用:统计当前目录下,文件的个数(包括子目录中的文件)ls-l......
  • T3/A40i支持Linux-5.10新内核啦,Docker、Qt、Python统统升级!
    自2021年创龙科技推出全志国产化率100%的T3/A40i工业核心板后,不到两年时间已超过800家工业客户选择创龙科技T3/A40i平台。随着客户产品的不断升级与迭代,部分“能源电力”、“工业自动化”行业客户对T3/A40i的Linux版本提出了更高要求,主要涉及Docker、Qt、Python等组件特性。秉持......
  • linux 打印my.txt文件的第10-15行
    #打印my.txt文件的第10-15行sed=streameditorsed-n'10,15p'my.txt#打印my.txt文件的第10-15行awk是三个人名字各取了一个字母awk'NR>=10&&NR<=20'my.txt常用的awk命令示例:打印文件的所有行:awk'{print$0}'filename打印文件的第10行到第20行:awk'NR>=10......
  • 如何根据Linux Kernel Mailing List打patch
    Linux内核正在不断开发和改进。每天的补丁都会提交到Linux内核邮件列表(LKML)。其中一些补丁被接受并合并到主流Linux内核中,供用户使用,而其他补丁则永远无法使用。有时从LKML获取补丁是有用的,例如,如果你在内核中开发,或者只是因为你想保持在前沿。另一个原因可能是,您需要向LKML提出......
  • linux磁盘操作
    磁盘常用接口类型IDE过时了SATApc用的最多服务器也用SCSI服务器专用SAS服务器专用磁盘分区 /boot swap 查看已经挂载的分区 #df-h 查看所有磁盘信息 #fdisk-l 单独查看指定磁盘信息 #fdisk-l/dev/sdb 分区操作 #fdisk/dev/sdb [root@ser......
  • VirtualBox 7.0.20 (macOS, Linux, Windows) - 开源跨平台虚拟化软件
    VirtualBox7.0.20(macOS,Linux,Windows)-开源跨平台虚拟化软件OracleVMVirtualBox7请访问原文链接:https://sysin.org/blog/virtualbox-7/,查看最新版。原创作品,转载请保留出处。VirtualBox简介VirtualBox是一款功能强大的x86和AMD64/Intel64虚拟化产品,适用于......
  • 子进程中的超时参数在 chainlit 应用程序中不起作用
    我有一个脚本可以为在Windows中运行的GraphRAG应用程序构建ChainlitUI。GraphRAG查询在终端中运行良好,尽管需要大约120秒(附有屏幕截图)。但是,当我运行此Chainlit脚本时,subprocess.run中的timeout=300无法按预期工作。相反,我在大约60秒后收到“无法到达服务器”错......