进程间通信

一、匿名管道(PiPe)

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>

int main(int argc, char const *argv[])
{   
    pid_t cpid;
    int pipefd[2];
    //将程序传递进来的第一个命令行参数 通过管道传输给子进程
    if (argc !=2)
    {
        fprintf(stderr,"%s:请填写需要传递的信息\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    //创建管道
    int pipeR = pipe(pipefd);
    if (pipeR == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    

    //复制父子进程
    cpid = fork();
    if (cpid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (cpid == 0)
    {
        //子进程 读取管道的数据 打印到控制台
        //关闭写功能
        close(pipefd[1]);
        char str[1024]={0};
        sprintf(str,"新学员%d接收信息\n",getpid());
        write(STDOUT_FILENO,&str,sizeof(str));
        char buf;
        while (read(pipefd[0],&buf,1)>0)
        {
            write(STDOUT_FILENO,&buf,1);
        }
        write(STDOUT_FILENO,"\n",1);
        //关闭读功能
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);
    }
    else
    {
        //父进程 写入管道数据 提供给子进程读
        //先关闭读功能
        close(pipefd[0]);
        //将数据写入到管道中
        printf("老学员%d对新学员%d传递信息\n",getpid(),cpid);
        write(pipefd[1],argv[1],strlen(argv[1]));
        //关闭写功能
        close(pipefd[1]);
        waitpid(cpid,NULL,0);
        exit(EXIT_SUCCESS);
    }
   
    return 0;
}

makefile

cpp 复制代码
unnamed_pipe_test:unnamed_pipe_test.c
	-$(CC) -o $@ $^
	-./$@ "test"
	-rm ./$@

二、有名管道

cpp 复制代码
//fifo_write.cpp内容
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<errno.h>

int main(int argc, char const *argv[])
{
    int fd;
    //有名管道创建完成之后 后续是可以重复使用的 不推荐重复使用 推荐每次用完之后释放
    char*pipe_path="/home/lxl/process_test/temp/myfifo";
    int fifoR=mkfifo(pipe_path,0664);
    if (fifoR!=0)
    {
        perror("mkfifo");
        if (errno != 17)//文件创建失败的时候退出
        {
            exit(EXIT_FAILURE);
        }
        
    }
    //对有名管道的特殊文件 创建fd
    fd = open(pipe_path,O_WRONLY);
    if (fd == -1)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }
    
    char buf[100];
    size_t read_num;
    //读取控制台数据写入到管道中
    while ((read_num = read(STDIN_FILENO,buf,100))>0)
    {
        write(fd,buf,read_num);
    }
    if(read_num < 0)
    {
        perror("read");
        close(fd);
        exit(EXIT_FAILURE);
    }
    printf("发送数据到管道完成,进程终止\n");
    close(fd);
    //释放管道
    //清除对应的特殊文件
    if (unlink(pipe_path)==-1)
    {
        perror("unlink");
    }
    
    return 0;
}
cpp 复制代码
//fifo_read.cpp内容
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<errno.h>

int main(int argc, char const *argv[])
{
    int fd;
    char*pipe_path="/home/lxl/process_test/temp/myfifo";;
    
    //对有名管道的特殊文件 创建fd
    fd = open(pipe_path,O_RDONLY);
    if (fd == -1)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }
    
    char buf[100];
    size_t read_num;
    //读取管道数据写入到控制台
    while ((read_num = read(fd,buf,100))>0)
    {
        write(STDOUT_FILENO,buf,read_num);
    }
    if(read_num < 0)
    {
        perror("read");
        close(fd);
        exit(EXIT_FAILURE);
    }
    printf("接收管道数据完成,进程终止\n");
    close(fd);

    return 0;
}

makefile

cpp 复制代码
fifo_write:fifo_write.c
	-$(CC) -o $@ $^

fifo_read:fifo_read.c
	-$(CC) -o $@ $^

三、共享内存

测试例程

展示如何使用mmap在父子进程之间共享信息

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/types.h>

int main(int argc, char const *argv[])
{
    int fd;
    char *share;
    //1、创建一个共享内存对象
    char shm_name[100]={0};
    sprintf(shm_name,"/letter%d",getpid());
    fd=shm_open(shm_name,O_RDWR | O_CREAT,0664);
    if (fd < 0)
    {
        perror("shm_open");
        exit(EXIT_FAILURE);
    }

    //2、设置共享内存对象大小
    ftruncate(fd,1024);
    

    //3、内存映射
    share=mmap(NULL,1024,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
    if (share == MAP_FAILED)
    {
       perror("mmap");
       exit(EXIT_FAILURE);
    }
    //映射完成之后,关闭fd连接,不是释放
    close(fd);
    //4、使用内存映射实现进程间的通讯
    pid_t pid = fork();
    if (pid < 0)
    {
       perror("fork");
       exit(EXIT_FAILURE);
    }
    if (pid==0)
    {
        //子进程
        strcpy(share,"你是个好人\n");
        printf("新学员%d完成回信\n",getpid());
    }
    else
    {
        //父进程
        sleep(1);
        waitpid(pid,NULL,0);
        printf("老学员%d收到新学员%d的回信:%s",getpid(),pid,share);
        //5、释放映射区
        int re=munmap(share,1024);
        if (re==-1)
        {
            perror("munamp");
            exit(EXIT_FAILURE);
        }
        
    }
    
    //释放共享内存对象
    shm_unlink(shm_name);
    return 0;
}
cpp 复制代码
shared_memory:shared_memory.c
	-$(CC) -o $@ $^ -lrt
	-./$@
	-rm ./$@

四、消息队列

1、相关系统调用

(1)mq_open()

(2)mq_timedsend()

(3)mq_timedrecceive()

(4)mq_unlink()

(5)clock_gettime()

2、父子进程间通信测试例程

cpp 复制代码
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<mqueue.h>
#include<time.h>
#include<stdlib.h>

int main(int argc, char const *argv[])
{
    //创建消息队列
    struct mq_attr attr;
    //有用的参数,表示消息队列的容量
    attr.mq_maxmsg=10;
    attr.mq_msgsize=100;
    //被忽略的消息,在创建消息队列的时候用不到
    attr.mq_flags=0;
    attr.mq_curmsgs=0;

    char *mq_name="/father_son_mq";
    mqd_t mqdes= mq_open(mq_name,O_RDWR|O_CREAT,0664,&attr);

    if (mqdes == (mqd_t)-1)
    {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }
    
    //创建父子进程
    pid_t pid =fork();
    if (pid<0)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (pid == 0)
    {
        //子进程 等待接收消息队列中的消息
        char buf_receive [100];
        struct  timespec time_info;
        for (int i = 0; i < 10; i++)
        {
            //清空buf_receive
            memset(buf_receive,0,100);
            //设置接收数据的等待时间
            clock_gettime(0,&time_info);
            time_info.tv_sec += 5;
            //接收消息队列的数据 打印到控制台
            int receiveR = mq_timedreceive(mqdes,buf_receive,100,0,&time_info);
            if (receiveR == -1)
            {
                perror("mq_timedreceive报错");
            }
            printf("子进程接收到第%d消息:%s\n",i+1,buf_receive);

        }
        
    }
    else
    {
        //父进程 发送消息到消息队列中
        char buf_send[100];
        struct  timespec time_info;
        for (int i = 0; i < 10; i++)
        {
            //清空处理buf_send
            memset(buf_send,0,100);
            sprintf(buf_send,"父进程的第%d次发送消息\n",(i+1));
            //获取当前的具体时间
            clock_gettime(0,&time_info);
            time_info.tv_sec += 5;
            //发送消息
            int sendR=mq_timedsend(mqdes,buf_send,100,0,&time_info);
            if (sendR == -1)
            {
                perror("mq_timedsend");
            }
            printf("父进程发送一条消息,休眠1s\n");
            sleep(1);
        }
    }
    
    //最终不管是父进程还是子进程都需要释放消息队列的饮用
    close(mqdes);
    //清除消息队列只需要执行一次
    if(pid>0)
    {
        mq_unlink(mq_name);
    }

    return 0;
}

makefile

cpp 复制代码
father_son_mq_test:father_son_mq_test.c
	-$(CC) -o $@ $^ -lrt
	-./$@
	-rm ./$@

3、非父子进程通信案例

我们创建两个进程:生产者和消费者,前者从控制台接收数据并写入消息i队列,后者从消息队列接收数据并打印到控制台。

cpp 复制代码
//producer.cpp内容

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<mqueue.h>
#include<string.h>
#include<time.h>
#include<sys/stat.h>

int main(int argc, char const *argv[])
{
    //创建消息队列
    char*mq_name="/producer";
    struct  mq_attr atrr;
    atrr.mq_maxmsg=10;
    atrr.mq_msgsize=100;
    atrr.mq_flags=0;
    atrr.mq_curmsgs=0;
    
    mqd_t mqdes = mq_open(mq_name,O_RDWR|O_CREAT,0664,&atrr);
    
    if (mqdes == (mqd_t)-1)
    {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }
    char buf_write[100];
    struct timespec time_info;
    while(1)//循环挂起 一直读取控制台消息
    {
        //清除缓存区
        memset(buf_write,0,100);
        //读取控制台消息
        ssize_t readR= read(STDIN_FILENO,buf_write,100);
        clock_gettime(0,&time_info);
        time_info.tv_sec += 5;
        //如果报错
        if (readR == -1)
        {
            perror("read");
            continue;
        }
        else if(readR == 0)
        {
            //如果从控制台收到停止发送的消息
            //ctrl + d 关闭控制台输入
            printf("EOF,exit...\n");
            char eof=EOF;
            //将EOF当作一条消息发送到消息队列
            int sendR = mq_timedsend(mqdes,&eof,1,0,&time_info);
            if (sendR == -1)
            {
                perror("mq_timedsend");
            }
            break;
        }
        else
        {
            //正常读取控制台信息
            int sendR = mq_timedsend(mqdes,&buf_write,100,0,&time_info);
            if (sendR == -1)
            {
                perror("mq_timedsend");
                continue;
            }
            printf("从控制台读取到消息,已经发送给消息队列\n");
        }
        
    }

    //释放消息队列的引用
    close(mqdes);
    //清除消息队列------在消费者那里清除比较好
    //mq_unlink(mq_name);

    return 0;
}
cpp 复制代码
//comsumer.cpp内容
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<mqueue.h>
#include<time.h>
#include<sys/stat.h>

int main(int argc, char const *argv[])
{
    //打开生产者消息队列
    char *mq_name="/producer";
    mqd_t mqdsp = mq_open(mq_name,O_RDWR);
    if (mqdsp == (mqd_t)-1)
    {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }

    char buf_receive[100];
    struct timespec time_info;
    while (1)
    {
        //清空接收缓存区
        memset(buf_receive,0,100);
        clock_gettime(0,&time_info);
        time_info.tv_sec += 5;
        int recR = mq_timedreceive(mqdsp,&buf_receive,100,0,&time_info);
        if (recR == -1)
        {
            perror("mq_timedreceive");
            continue;
        }
        else
        {
            //正常接收信息
            if(buf_receive[0] == EOF)//读到消息队列信息末尾
            {
                printf("接收生产者信息结束,exit...\n");
                break;
            }
            else
            {
                printf("接收生产者的信息:%s\n",buf_receive);
            }
        }
    }

    //释放消息队列的引用
    close(mqdsp);
    //清除消息队列
    mq_unlink(mq_name);
    
    return 0;
}

makefile

cpp 复制代码
producer:producer.c
	-$(CC) -o $@ $^ -lrt

comsumer:comsumer.c
	-$(CC) -o $@ $^ -lrt

打开两个终端实现信息通讯

五、信号

信号处理例程

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>

void signal_handler(int signalnum)
{
    printf("\n接收到信号%d,停止程序\n",signalnum);
    exit(EXIT_SUCCESS);
}

int main(int argc, char const *argv[])
{
    if(signal(SIGINT,signal_handler) == SIG_ERR)
    {
        perror("signal");
        return 1;
    }

    while(1)
    {
        sleep(1);
        printf("你好,在吗?\n");
    }
    return 0;
}

makefile

cpp 复制代码
signal_test:signal_test.c
	-$(CC) -o $@ $^ 
	-./$@
	-rm ./$@
相关推荐
yzwlord1 小时前
【无标题】
linux·运维·rust·ssh
晚风_END1 小时前
Linux|操作系统|最新版zfs编译后的适用于centos7的rpm安装包完全离线安装介绍
linux·运维·服务器·c++·python·缓存·github
黑猫学长呀2 小时前
存储宝典第4篇:存储芯片中常说的E2E是啥?
linux·单片机·嵌入式硬件·e2e·ssd·ufs·存储芯片
实心儿儿2 小时前
Linux —— 进程间通信 - system V进程间通信 - 共享内存(2)
linux·服务器
烛衔溟2 小时前
TypeScript 类实现接口
linux·ubuntu·typescript
小则又沐风a2 小时前
深入了解进程概念 第二章
java·linux·服务器·前端
CCPC不拿奖不改名2 小时前
PostgreSQL数据库部署linux服务器流程
linux·服务器·数据库·windows·python·docker·postgresql
lzh200409192 小时前
手搓一个简易 Linux 进程池:巩固进程知识
linux·c++
xiaoye-duck3 小时前
《Linux系统编程》Linux基础开发工具 (一):软件包管理器yum/apt,编辑器Vim,编译器GCC/G++
linux