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)方法:管道、信号量、共享内存、消息队列、套接字

一、有名管道

管道可以用来在两个进程之间传递数据,如: ps -ef | grep bash, 其中|就是管道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。管道通信方式为 半双工点对点通信
在这里插入图片描述
创建管道文件命令:mkfifo <文件名>
在这里插入图片描述

  1. 写入数据时,写指针后移;读取数据时,读指针后移(两指针在内存中循环移动)
  2. 管道文件可读数据为读写指针之间的数据
  3. 每次读取的数据量为min(管道可读数据量期望读的数据量)
  4. 当读指针追上写指针,表示管道文件读空了;当写指针追上读指针,表示管道文件写满了;

读进程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main(){
    int fd = open("fifo", O_RDONLY);
    if(fd == -1){
        exit(0);
    }
    printf("fd = %d\n", fd);

    while(1){
        char buff[128] = {0};
        // 对于管道文件,写入一次,不会被同一进程读取数据
        int n = read(fd, buff, 128);
        printf("n = %d\n", n);
        if(n == 0){
            // 返回值为0,说明写进程关闭管道文件
            // 阻塞说明等待写进程写入
            break;
        }
        printf("%s", buff);
    }
    close(fd);
    exit(0);
}

写进程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<signal.h>

void sig_fun(int sig){
    printf("sig = %d\n", sig);
}

int main(){
    signal(SIGPIPE, sig_fun);
    int fd = open("fifo", O_WRONLY);
    if(fd == -1){
        exit(0);
    }
    printf("fd = %d\n", fd);
    while(1){
        char buff[128] = {0};
        printf("input:");
        fflush(stdout);
        fgets(buff, 128, stdin);
        if(strcmp(buff, "end\n") == 0){
            break;
        }
        // 当读进程关闭时,再写入则收到内核发送的SIGPIPE,导致程序结束
        int n = write(fd, buff, strlen(buff));
    }
    close(fd);
    exit(0);
}

管道文件在内存里,不在硬盘里,显示永远为0
在这里插入图片描述

二、无名管道

父子进程可利用无名管道进行通信,fork后管道文件描述符的引用计数分别加1

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(){
    // fd[0]:读 fd[1]: 写
    int fd[2];
    if(pipe(fd) == -1){
        printf("create pipe error\n");
        exit(0);
    }

    // 创建子进程
    pid_t pid = fork();
    // fork后读写端的文件描述符的引用计数分别加1
    if(pid == -1){
        close(fd[0]);
        close(fd[1]);
        exit(0);
    }

    if(pid == 0){
        // 子进程读数据,关闭写端,即写端的引用计数减1
        close(fd[1]);
        char buff[128] = {0};
        read(fd[0], buff, 128);
        printf("child read : %s\n", buff);
        close(fd[0]);
    }else{
        // 父进程写数据,关闭读端,即读端的引用计数减1
        close(fd[0]);
        write(fd[1], "hello", 5);
        close(fd[1]);
    }
    exit(0);
}

管道特点:

  • 无论有名还是无名,写入管道的数据都在内存中
  • 管道是一种半双工通信方式(通信方式有单工、半双工、全双工)
  • 有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间

无名管道创建后只有2个文件描述符,一般是通过fork给子进程进行通信;而对于有名管道,只要能open都可以进行读或写

三、信号量

临界资源: 同一时刻,只允许被一个进程或线程访问的资源
临界区: 访问临界资源的代码段

使用信号量实现3个进程打印:ABCABCABC…
sem.h

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>

#define SEM_NUM 3
#define SEM1 0
#define SEM2 1
#define SEM3 2

union semun{
    int val;
};

void sem_init();

// 操作第几个信号量
void sem_p(int index);

void sem_v(int index);

void sem_destroy();

sem.c

#include "sem.h"

static int semid = -1;

void sem_init(){
    // 信号量初始值
    int arr[SEM_NUM] = {1,0,0};
    // 全新创建信号量集合(玩游戏建房)
    semid = semget((key_t)2345, SEM_NUM, IPC_CREAT|IPC_EXCL|0600);
    if(semid == -1){
        // 若信号量已经被创建了,则全新创建失败
        // 现根据key_gen获取已有信号量
        semid = semget((key_t)2345, SEM_NUM, 0600);
        if(semid == -1){
            printf("semget error\n");
            return;
        }
    }else{
        // 全新创建成功,需要对信号量进行初始化
        union semun a;
        for(int i=0; i<SEM_NUM; i++){
            a.val = arr[i];
            // 给信号量集合semid中的i号信号量设置初始值a
            if(semctl(semid, i, SETVAL, a) == -1){
                printf("semctl setval\n");
            }
        }
    }
}

void sem_p(int index){
    if(index < 0 || index >= SEM_NUM){
        printf("index out of range");
        return ;
    }
    struct sembuf buf;
    // 信号量集合的索引
    buf.sem_num = index;
    // 对信号量的操作
    buf.sem_op = -1;
    // 异常中止时,系统自动归还资源
    buf.sem_flg = SEM_UNDO;
    // 进行PV操作,1表示从&buf开始只有一个元素
    if(semop(semid, &buf, 1) == -1){
        printf("semop error");
    }
}

void sem_v(int index){
    if(index < 0 || index >= SEM_NUM){
        printf("index out of range");
        return ;
    }
    struct sembuf buf;
    // 信号量集合的索引
    buf.sem_num = index;
    // 对信号量的操作
    buf.sem_op = 1;
    // 异常中止时,系统自动归还资源
    buf.sem_flg = SEM_UNDO;
    // 进行PV操作
    if(semop(semid, &buf, 1) == -1){
        printf("semop error");
    }
}

void sem_destroy(){
    // 信号量集合id,信号量集合下标
    // 销毁只看id和命令,不看第二个参数:下标
    if(semctl(semid, 0, IPC_RMID) == -1){
        printf("semctl IPC_RMID error");
    }
}

a.c

#include "sem.h"

int main(){
    sem_init();
    for(int i=0; i<5; i++){
        sem_p(SEM1);
        printf("A");
        fflush(stdout);
        sem_v(SEM2);
        sleep(1);
    }
}

b.c

#include "sem.h"

int main(){
    sem_init();
    for(int i=0; i<5; i++){
        sem_p(SEM2);
        printf("B");
        fflush(stdout);
        sem_v(SEM3);
        sleep(1);
    }
}

c.c

#include "sem.h"

int main(){
    sem_init();
    for(int i=0; i<5; i++){
        sem_p(SEM3);
        printf("C");
        fflush(stdout);
        sem_v(SEM1);
        sleep(1);
    }
    // 最后打印C,所以由c.c销毁
    sem_destroy();
}

ipcs -s :查看信号量
ipcrm -s <sem-id>:删除信号量
在这里插入图片描述

四、共享内存

共享内存为多个进程之间共享和传递数据提供了一种有效的方式。

共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是由 malloc 分配的一样,无需拷贝,就是使用的公共内存

如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。

在这里插入图片描述

五、消息队列

FIFO

消息队列独立于进程存在,只要写入了,消息一直都在(除非读取),和程序是否运行没有关系,消除在同步命名管道的打开和关闭时可能出现的问题。

需要添加消息类型,才能写入消息队列,以消息为单位进行写入和读取

读取的时候,需要指定消息类型,同一消息类型的数据块按顺序读取,直到消息队列中此类型的消息被读空。在函数中,也可指定消息类型为0,表示所有类型的数据都可以读取。
在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-01 12:21:05  更:2021-09-01 12:21:09 
 
开发: 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/15 11:58:24-

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