IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux进程通信 -> 正文阅读

[系统运维]linux进程通信

IPC (interProcess Communication) 进程间通信,通过内核提供的缓冲区,进行数据的交换机制。

IPC通信的几种方式:

  • pipe 管道 最简单
  • fifo 有名管道
  • mmap 文件映射共享IO, 速度最快
  • 本地socket最稳定
  • 信号 携带的信号量最小
  • 共享内存
  • 消息队列

1. 管道pipe

  • 管道通信优劣:
    优点:简单,相比信号和套接字实现进程通信简单很多
    缺点:1. 只能单向通信(半双工),双向通信需要建立两个管道?2. 只能用于父子、兄弟(有共同祖先)进程之间的通信。该问题可以有FIFO有名管道解决。

  • man pipe 查看函数

    #include <unistd.h>
    int pipe(int pipefd[2]);
    
    #define _GNU_SOURCE             /* See feature_test_macros(7) */
    #include <fcntl.h>              /* Obtain O_* constant definitions */
    #include <unistd.h>
    int pipe2(int pipefd[2], int flags);
    
  • 进程通信demo

    #include <stdio.h>
    #include <unistd.h>
    int main()
    {
        printf("Begin ..\n");
        int fd[2];
        pipe(fd); // 先创建管道,再创建子进程
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程
            write(fd[1], "hello", 5);
        } else if(pid > 0) {
            // 父进程
            char buff[12] = {0};
            int ret = read(fd[0], buff, sizeof(buff)); // 阻塞等待
            if(ret > 0) {
                write(STDOUT_FILENO, buff, ret);
            }
        }
        printf("End ...\n");
        return 0;
    }
    
  • 读管道
    写端全部关闭 --read读到0,相当于读到文件末尾
    写端没有完全关闭,有数据 read 读到数据,没有数据 read 阻塞 fcntl函数可以更改非阻塞
    写端全部关闭demo

    #include <stdio.h>
    #include <unistd.h>
    int main()
    {
        printf("Begin ..\n");
        int fd[2];
        pipe(fd); // 先创建管道,再创建子进程
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程
            sleep(3);
            close(fd[0]); // 关闭读端
            write(fd[1], "hello", 5);
            close(fd[1]); // 关闭写端
            sleep(10);
        } else if(pid > 0) {
            // 父进程
            char buff[12] = {0};
            close(fd[1]); // 关闭写端
            while (1) {
                int ret = read(fd[0], buff, sizeof(buff)); // 阻塞等待
                if (ret == 0) { // 如果写端全部关闭(所有写端),read会读到0
                    printf("\n read over! \n");
                    break;
                }
                if(ret > 0) {
                    write(STDOUT_FILENO, buff, ret);
                }
            }
        }
        printf("End ...\n");
        return 0;
    }
    

    输出:

    Begin ..
    hello
     read over! 
    End ...
    
  • 写管道
    读端全部关闭 产生一个信号SIGPIPE,程序异常终止
    读端未全部关闭,管道已满 write 阻塞,管道未满 write 正常写入

    读端全部关闭程序

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    int main()
    {
        printf("Begin ..\n");
        int fd[2];
        pipe(fd); // 先创建管道,再创建子进程
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程
            sleep(3);
            close(fd[0]); // 关闭读端
            write(fd[1], "hello", 5);
            close(fd[1]); // 关闭写端
            sleep(10);
        } else if(pid > 0) {
            // 父进程
            char buff[12] = {0};
            close(fd[1]); // 关闭写端
            close(fd[0]); // 关闭读端
            int status;
            wait(&status);
            if (WIFSIGNALED(status)) {
                printf("killed by %d\n", WTERMSIG(status));
            }
            while (1) {
                int ret = read(fd[0], buff, sizeof(buff)); // 阻塞等待
                if (ret == 0) {
                    printf("\n read over! \n");
                    break;
                }
                if(ret > 0) {
                    write(STDOUT_FILENO, buff, ret);
                }
            }
        }
        printf("End ...\n");
        return 0;
    }
    

    输出结果

    Begin ..
    killed by 13
    

    补充: ulimit -a 查看所有系统资源的上限

2. FIFO有名管道

区分与pipe,pipe只能再有血缘关系的进程之间通信,而fifo可以实现非血缘关系的进程之间的通信。
FIFO是linux系统基础文件的一种,但是,FIFO文件在磁盘上没有数据块,仅仅用来表示内核中一条通道。各个进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。

  • 创建一个管道伪文件
    mkfifo myfifo 创建命令
    也可以使用函数int mkfifo(cosnt char* pathname, mode_t mode);

  • 内核会针对fifo文件开辟一个缓冲区,操作fifo文件,实现进程间的通信。(实际上就是文件读写)

  • FIFO写数据demo

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        if (argc != 2) {
            printf("./a.out fifoname\n");
        }
        // 当前目录有一个myfifo文件
        // 打开fifo文件
        int fd = open(argv[1], O_WRONLY);
        // 写数据
        char buf[256];
        int num = 1;
        while (num < 100) {
            memset(buf, 0x00, size(buf));
            sprintf(buf, "xiaoming%02d", num++);
            write(fd, buf, strlen(buf));
            sleep(1);
    	}
        close(fd);
        return 0;
    }
    

    FIFO读数据demo

    int main(int argc, char* argv[])
    {
        if (argc != 2) {
            printf("./a.out fifoname\n");
        }
        // 当前目录有一个myfifo文件
        // 打开fifo文件
        int fd = open(argv[1], O_WRONLY);
        char buf[256];
        int ret;
        while (1){
            // 循环读
            memset(buf, 0x00, sizeof(buf));
            ret = read(fd, buf, sizeof(buf));
            if (ret < 0) {
                printf("read:%s", buf);
            }
        }
        close(fd);
        return 0;
    }
    

    注:打开fifo文件的时候,read会阻塞等待write端open,write端也会阻塞等待另一端打开。

3. mmap

  • 函数
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
int munmap(void *addr, size_t length);

创建映射区 mmap 函数参数:

  • addr: 传 NULL
  • length: 映射区长度
  • prot:
    PROT_READ 可读
    PROT_WRITE 可写
  • flags:
    MAP_SHARED 共享的(对内存的修改会影响源文件)
    MAP_PRIVATE 私有的
  • fd 文件描述符,open打开一个文件
  • offset 偏移量
  • 返回值
    成功, 返回可用的内存首地址
    失败, 返回 MAP_FAILED

释放映射区 munmap函数:

  • addr 传 mmap返回值
  • length mmap创建的长度
  • 返回值
    成功返回 0
    失败返回 -1

创建映射区程序demo

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main()
{
    int fd = open("mem.txt", O_RDWR);
    // 创建映射区
    char *mem = (char *)mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, 0);
    if (mem == MAP_FAILED) {
        perror("mmap err");
        return -1;
    }
    strcpy(mem, "hello");

    //释放mmap
    munmap(mem, 8);
    close(fd);
    return 0;
}
  • mmap 9问
    在这里插入图片描述

  • 使用mmap实现父子进程之间通信

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    int main()
    {
        int fd = open("mem.txt", O_RDWR);
        // 创建映射区
        char *mem = (char *)mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (mem == MAP_FAILED) {
            perror("mmap err");
            return -1;
        }
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程
            *mem = 'a';
            printf("child, *mem = %s\n", mem);
            sleep(3); // 等待父进程修改
            printf("child, *mem = %s\n", mem);
        } else if (pid > 0) {
            // 父进程
            sleep(1); // 等待子进程修改
            printf("parent, *mem = %s\n", mem);
            *mem = 'b';
            printf("parent, *mem = %s\n", mem);
            wait(NULL);
        }
    
        //释放mmap
        munmap(mem, 8);
        close(fd);
        return 0;
    }
    
    
    • 输出结果
    child, *mem = a
    parent, *mem = a
    parent, *mem = b
    child, *mem = b
    

结论:父子进程共享,1. 打开文件; 2. mmap建立映射区(但是必须使用)

  • 匿名映射
    通过使用mmap发现,使用映射区完成文件的读写非常方便,父子进程间通信也比较容易。缺陷是,每次创建一个映射区必须依赖一个文件才能实现。通过创建一个映射区要open一个temp文件,最好还要close,比较麻烦。使用匿名映射无需依赖一个文件即可创建一个映射区,需要借助标志位参数flags来指定。
    在这里插入图片描述
  • 匿名映射程序demo
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
    // 创建映射区
    char *mem = (char *)mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED, MAP_ANON, -1);
    if (mem == MAP_FAILED) {
        perror("mmap err");
        return -1;
    }
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        *mem = 'a';
        printf("child, mem = %s\n", mem);
        sleep(3); // 等待父进程修改
        printf("child, mem = %s\n", mem);
    } else if (pid > 0) {
        // 父进程
        sleep(1); // 等待子进程修改
        printf("parent, mem = %s\n", mem);
        *mem = 'b';
        printf("parent, mem = %s\n", mem);
        wait(NULL);
    }

    //释放mmap
    munmap(mem, 8);
    return 0;
}

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 16:13:57  更:2021-11-15 16:16:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:28:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码