Linux高并发服务器开发 第十七天(管道缓存区查询大小 管道的优劣 命名管道mkfifo 建立释放映射区mmap/munmap 匿名映射 进程间的通信)

目录

1.pipe管道读写行为

[1.1例题:实现父子进程 ls | wc -l](#1.1例题:实现父子进程 ls | wc -l)

[1.2兄弟进程 ls | wc -l](#1.2兄弟进程 ls | wc -l)

2.管道缓存区

2.1命令查询

2.2函数查询

3.pipe管道的优劣

[4.命名管道 fifo](#4.命名管道 fifo)

5.mmap

5.1文件进程间通信

5.2建立、释放映射区

5.3匿名映射

6.进程间通信

6.1父子进间通信

6.2无血缘关系进程间通信


1.pipe管道读写行为

  • 读管道:
  1. 管道有数据, read返回实际读到的字节数。

  2. 管道无数据,1)无写端,read 返回 0 (类似读到文件末尾)

​ 2)有写端,阻塞等待。

  • 写管道:
  1. 无读端,异常终止。 ( SIGPIPE 信号)

  2. 有读端,1)管道已满,阻塞等待。

​ 2)管道未满,返回实际写出的字节数。

1.1例题:实现父子进程 ls | wc -l

父进程执行 ls 命令并将其输出写入管道,子进程执行 wc -l 并从管道读取输入

cpp 复制代码
int main(int argc, char *argv[])
{
    pid_t pid;
    int fd[2];
    
    // 先创建pipe
    pipe(fd);
    pid = fork();            // ls | wc -l

    if (pid == 0) {  // 子进程 实现 wc -l
        close(fd[1]);        // 子进程读管道,关闭写端.
        dup2(fd[0], STDIN_FILENO);        // 让 wc 从管道的读端,读数据.
        execlp("wc", "wc", "-l", NULL);
        
    } else if (pid > 0) {
        close(fd[0]);        // 父进程写管道,关闭读端.
        dup2(fd[1], STDOUT_FILENO);// 将 写出到 屏幕的ls 结果,写入到 管道写端.
        execlp("ls", "ls", NULL);
    }
    return 0;
}

1.2兄弟进程 ls | wc -l

cpp 复制代码
int main(int argc, char *argv[])
{
    int fd[2], i = 0;
    pid_t pid;
    
    pipe(fd);

    for (i = 0; i < 2; i++)
        if ((pid = fork()) == 0) {
            break;
        }
    if (i == 0) {        // 兄        ls
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        execlp("ls", "ls", NULL);
    } else if (i == 1) {        // 弟    wc -l
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        execlp("wc", "wc","-l",NULL);
    } else {        // 父
        close(fd[0]);// 父进程不参与管道使用,应该关闭写端、读端。保证管道内部数据单向流动。
        close(fd[1]);
        for (i = 0; i<2; i++)
            wait(NULL);
    }
    return 0;
}

2.管道缓存区

2.1命令查询

2.2函数查询

long fpathconf(int fd, int name);

参1 :传 fd[0]/fd[1] 都可以!

参2 :传 _PC_PIPE_BUF 宏

3.pipe管道的优劣

  • 优点:简单。比信号、套接字、mmap 简单很多!

  • 缺点:

  1. 只能单向通信,实现双向通信,需要两个管道。

  2. 只能应用于父子、兄弟...(有公共祖先)进程间。无血缘关系进程间,后来用 fifo 替代。

4.命名管道 fifo

  • 命令创建:mkfifo 管道名

  • 函数创建:

//可以用于无血缘关系进程间通信

int mkfifo(const char *pathname, mode_t mode);

------ 演示代码:fifo_w.c fifo_r.c

  • 管道中的数据,一次性读取,读走没。

  • 读端:以 O_RDONLY 打开 fifo 管道。

  • 写端:以 O_WRONLY/O_RDWR 打开同一个 fifo 管道。

5.mmap

5.1文件进程间通信

  • 有血缘关系、无血缘关系的进程,都可以使用同一个文件来实现进间通信。

5.2建立、释放映射区

  • mmap 借助文件映射,创建共享内存映射区

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数:

addr:指定映射区的首地址。通常传NULL,表示让系统自动分配

length:共享内存映射区的大小。(<=文件的实际大小。)

prot:共享内存映射区的读写属性。PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE

flags:标注共享内存映射区的共享属性:

MAP_SHARED: 对共享内存所做的修改,会反应到物理磁盘文件上。 IPC专用!

MAP_PRIVATE:对共享内存所做的修改,不会反应到物理磁盘文件上。

fd: 用来创建共享内存映射区的那个文件的 文件描述符。

offset:默认0,表示映射文件全部!偏移位置。必须是4k整数倍。

返回值:

成功:映射区的首地址。

失败:MAP_FAILED (void *(-1)), errno

  • munmap 释放共享内存映射

int munmap(void *addr, size_t length);

参1:mmap() 函数的返回值。

参2:共享内存映射区大小

返回值:

成功:0

失败:-1, errno

mmap使用的注意事项:

  1. 用于创建映射区的文件的大小,必须是非0。映射区的大小 <= 文件大小。

  2. 创建映射区,需要read权限。指定访问权限为 MAP_SHARED, mmap需要读写权限。 应该 <= 文件打开权限。只写不行!

  3. 文件描述符fd, 在mmap创建映射区完成,可以立即关闭!后续访问文件使用 内存地址。

  4. offset 必须是 4096 的整数倍。(MMU映射的最小单位 4k)

  5. 映射区访问的权限设为 MAP_PRIVATE, 对内存做的所有修改,都只在内存有效,不反应的磁盘上。不能应用于 IPC

mmap函数保险调用方式:

  1. fd = open("文件名",O_RDWR);

  2. mmap(NULL, 实际有效文件大小, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

mmap特性

  • fifo、mmap 都可以应用于非血缘关系进程间通信。区别:

  • mmap:数据可以重复读写。

  • fifo:数据只能一次性读写。

  • 直接操作内存,执行速度快!

5.3匿名映射

  • 只能应用于,有血缘关系的进程间通信

p = (int *)mmap(NULL, 400, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

  • MAP_ANONYMOUS 、MAP_ANON 只在 Linux 系统中有效。

  • /dev/null : 设备文件。黑洞文件。特性:无限向该文件写数据。写入没!

  • /dev/zero: 设备文件。特性:无限从该文件读取数据。读多少,有多少。读到的都是"\0"

  • 类unix系统中没有 MAP_ANONYMOUS 、MAP_ANON 选项,可以借助 /dev/zero 实现。

cpp 复制代码
 fd = open("/dev/zero", O_RDWR);
 p = mmap(NULL, size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0);

6.进程间通信

6.1父子进间通信

  1. 父进程 先创建 映射区。 open(O_RDWR); mmap(MAP_SHARED);

  2. fork() 创建子进程。

  3. 一个进程使用 映射区读,另一个进程写。

6.2无血缘关系进程间通信

  1. 两个进程,打开同一个文件,创建映射区。

  2. 指定 flags 为 MAP_SHARED。

  3. 一个进程使用 映射区读,另一个进程写。

相关推荐
wqqqianqian4 分钟前
国产linux系统(银河麒麟,统信uos)使用 PageOffice自定义Word模版中的数据区域
linux·word·自定义·pageoffice·数据区域
小疆智控11 分钟前
数字化工厂升级引擎:Modbus TCP转Profinet网关助力打造柔性生产系统
服务器·网络·tcp/ip
南棱笑笑生16 分钟前
20250512给NanoPi NEO core开发板在Ubuntu core20.04系统更新boot.img
linux·运维·ubuntu
XMYX-019 分钟前
Linux du 命令终极指南:从基础到精通
linux·服务器
小锋学长生活大爆炸34 分钟前
【教程】Docker更换存储位置
运维·docker·容器
DourPanda38 分钟前
polarctf-web-[rce1]
linux·网络协议·网络安全
purrrew40 分钟前
【Java ee初阶】IP协议
服务器·网络协议·tcp/ip
一叶屋檐43 分钟前
Neo4j 图书馆借阅系统知识图谱设计
服务器·数据库·cypher
愚润求学1 小时前
【Linux】动静态库链接原理
linux·运维·服务器·开发语言·笔记
云攀登者-望正茂2 小时前
无缝部署您的应用程序:将 Jenkins Pipelines 与 ArgoCD 集成
运维·jenkins·argocd