• 进程间并发通信-IO多路复用


    1 select

    1.1 源码示例

    /*************************************************************************
    	> File Name: write.c
    	> Author: yas
    	> Mail: rage_yas@hotmail.com
    	> Created Time: 2024年06月02日 星期日 14时50分23秒
     ************************************************************************/
    
    #include
    
    #include 
    #include  
    
    #include 
    
    #include 
    
    #include 
    
    
    int main(void)
    {
    	int fd=0;
    	char tmpbuff[4096]={0};
    
    	/* 1 创建有名管道 */
    	mkfifo("/tmp/myfifo",0664);
    
    	/* 2 打开有名管道 */
    	fd=open("/tmp/myfifo",O_WRONLY);
    	if(-1==fd)
    	{
    		perror("fail to open");
    		return -1;
    	}
    
    	/* 3 接收终端输入并写入管道文件描述符-向管道写数据 */
    	while(1)
    	{
    		fgets(tmpbuff,sizeof(tmpbuff),stdin);
    		write(fd,tmpbuff,strlen(tmpbuff));
    	}
    
    	/* 4 关闭管道 */
    	close(fd);
    
    	return 0;
    }
    
    /*************************************************************************
    	> File Name: read.c
    	> Author: yas
    	> Mail: rage_yas@hotmail.com
    	> Created Time: 2024年06月02日 星期日 14时34分49秒
     ************************************************************************/
    
    #include
    
    #include 
    
    #include 
    #include  
    
    #include 
    
    #include 
    
    #include 
    
    /* According to POSIX.1-2001, POSIX.1-2008 */
    #include 
    /* According to earlier standards */
    #include 
    #include 
    #include 
    
    
    int main(void)
    {
        int fd=0;
        
        fd_set rdfds;
        fd_set tmpfds;
    
        int ret=0;
    
        char tmpbuff[4096]={0};
    
        /* 1 创建有名管道 */
        mkfifo("/tmp/myfifo",0664);
        
        /* 2 打开有名管道 */
        fd=open("/tmp/myfifo",O_RDONLY);
        if(-1==fd)
        {
            perror("fail to open");
            return -1;
        }
    
        /* 3 文件描述符集的操作 - 4个宏 - FD_ZERO(initialize)-FD_SET(create)-FD_CLR(delete)-FD_ISSET(retrieve) */
        /* 3.1 初始化文件描述符集合 */
        FD_ZERO(&rdfds);//清除文件描述符集合 - 初始化文件描述符集合
        /* 3.2 增加文件描述符至文件描述符集合 */
        FD_SET(fd,&rdfds);//添加文件描述符至文件描述符集
        FD_SET(0,&rdfds);
    
        /* 4 管道读(写) - 终端和管道-谁来数据就读谁 */
        while(1)
        {
            /* 4.1 初始化监听文件描述符集合 */
            tmpfds=rdfds;
            /* 4.2 监听文件描述符集合 -> 监听多个文件描述符,直到有一个或者多个文件描述符准备进行某类IO操作 */
            /*注意: 一旦监听到某个文件描述符准备进行IO操作,那么这个文件描述符监听前后状态就发生了变化 ( 非ready状态->ready状态 ),
             * 所以要想一直对某个文件描述符进行监听,每次调用select之前都要重置该文件描述符状态为监听之前的状态(即非ready状态)*/
            ret=select(fd+1,&tmpfds,NULL,NULL,NULL);
            if(-1==ret)
            {
                perror("fail to select");
                return -1;
            }
    
            /* 4.3 查询文件描述符集合中某个文件描述符状态是否发生改变 */
            if(FD_ISSET(fd,&tmpfds))
            {
                memset(tmpbuff,0,sizeof(tmpbuff));
                read(fd,tmpbuff,sizeof(tmpbuff));
                printf("fifo: %s\n",tmpbuff);
            }
    
            if(FD_ISSET(0,&tmpfds))
            {
                memset(tmpbuff,0,sizeof(tmpbuff));
                fgets(tmpbuff,sizeof(tmpbuff),stdin);
                printf("stdin: %s\n",tmpbuff);
            }
        }
    
        /* 5 关闭文件描述符 */
        close(fd);
    
        return 0;
    }
    

    1.2 运行结果

    1.3 分析总结

    2 poll

    1.1 源码示例

    /*************************************************************************
    	> File Name: write.c
    	> Author: yas
    	> Mail: rage_yas@hotmail.com
    	> Created Time: 2024年06月02日 星期日 16时30分10秒
     ************************************************************************/
    
    #include
    
    #include 
    #include  
    
    #include 
    
    #include 
    
    #include 
    
    
    int main(void)
    {
    	int fd=0;
    	char tmpbuff[4096]={0};
    
    	/* 1 创建有名管道 */
    	mkfifo("/tmp/myfifo",0664);
    
    	/* 2 打开有名管道 */
    	fd=open("/tmp/myfifo",O_WRONLY);
    	if(-1==fd)
    	{
    		perror("fail to open");
    		return -1;
    	}
    
    	/* 3 接收终端输入并写入管道文件描述符-向管道写数据 */
    	while(1)
    	{
    		fgets(tmpbuff,sizeof(tmpbuff),stdin);
    		write(fd,tmpbuff,strlen(tmpbuff));
    	}
    
    	/* 4 关闭管道 */
    	close(fd);
    
    	return 0;
    }
    
    /*************************************************************************
    	> File Name: read.c
    	> Author: yas
    	> Mail: rage_yas@hotmail.com
    	> Created Time: 2024年06月02日 星期日 16时30分10秒
     ************************************************************************/
    
    #include
    
    #include 
    #include  
    
    #include 
    
    #include 
    
    #include 
    
    #include 
    
    int main(void)
    {
        int fd=0;
    
        struct pollfd fds[2];
    
        int fready=0;
    
        char tmpbuff[4096]={0};
    
        /* 1 创建有名管道 */
        mkfifo("/tmp/myfifo",0664);
    
        /* 2 打开有名管道 */
        fd=open("/tmp/myfifo",O_RDONLY);
        if(-1==fd)
        {
            perror("fail to open");
            return -1;
        }
    
        /* 3 初始化文件描述符状态结构体数组*/
        fds[0].fd=fd;//添加文件描述符至数组
        fds[0].events=POLLIN;//修改文件描述符状态为 准备读
        fds[1].fd=0;
        fds[1].events=POLLIN;
    
        /* 4 管道读(写) - 终端和管道-谁来数据就读谁 */
        while(1)
        {
            /* 4.1 监听文件描述符状态结构体数组 */
            fready=poll(fds,2,-1);//文件描述符数组(数组)-文件描述符个数(数组长度)-超时时间(-1 一直等)
            if(-1==fready)
            {
                perror("fail to poll");
                return -1;
            }
    
            /* 4.2 查询文件描述符状态 - 通过位掩码方式查询 - 置位 */
            if(fds[0].revents&POLLIN)
            {
                memset(tmpbuff,0,sizeof(tmpbuff));
                read(fd,tmpbuff,sizeof(tmpbuff));
                printf("fifo: %s\n",tmpbuff);
            }
    
            if(fds[1].revents&POLLIN)
            {
                memset(tmpbuff,0,sizeof(tmpbuff));
                fgets(tmpbuff,sizeof(tmpbuff),stdin);
                printf("stdin: %s\n",tmpbuff);
            }
        }
    
        /* 5 关闭管道 */
        close(fd);
    }
    

    1.2 运行结果

    1.3 分析总结

    3 epoll

    1.1 源码示例

    /*************************************************************************
    	> File Name: write.c
    	> Author: yas
    	> Mail: rage_yas@hotmail.com
    	> Created Time: 2024年06月02日 星期日 17时22分26秒
     ************************************************************************/
    
    #include
    
    #include 
    #include  
    
    #include 
    
    #include 
    
    #include 
    
    
    int main(void)
    {
    	int fd=0;
    	char tmpbuff[4096]={0};
    
    	/* 1 创建有名管道 */
    	mkfifo("/tmp/myfifo",0664);
    
    	/* 2 打开有名管道 */
    	fd=open("/tmp/myfifo",O_WRONLY);
    	if(-1==fd)
    	{
    		perror("fail to open");
    		return -1;
    	}
    
    	/* 3 接收终端输入并写入管道文件描述符-向管道写数据 */
    	while(1)
    	{
    		fgets(tmpbuff,sizeof(tmpbuff),stdin);
    		write(fd,tmpbuff,strlen(tmpbuff));
    	}
    
    	/* 4 关闭管道 */
    	close(fd);
    
    	return 0;
    }
    
    /*************************************************************************
    	> File Name: read.c
    	> Author: yas
    	> Mail: rage_yas@hotmail.com
    	> Created Time: 2024年06月02日 星期日 17时22分26秒
     ************************************************************************/
    
    #include
    
    #include 
    #include  
    
    #include 
    
    #include 
    
    #include 
    
    #include 
    
    
    int main(void)
    {
        int fd=0;
    
        int epfd=0;
    
        struct epoll_event env;
    
        int fready=0;
    
        int i=0;
    
        struct epoll_event retenv[2];
    
        char tmpbuff[4096]={0};
    
    
        /* 1 创建有名管道 */
        mkfifo("/tmp/myfifo",0664);
    
        /* 2 打开有名管道 */
        fd=open("/tmp/myfifo",O_RDONLY);
        if(-1==fd)
        {
            perror("fail to open");
            return -1;
        }
    
        /* 3 创建内核事件表 - 并返回表头 */
        epfd=epoll_create(2);//预期添加到事件表的文件描述符数量
        if(-1==epfd)
        {
            perror("fail to epoll_create");
            return -1;
        }
    
        /* 4 初始化内核epoll事件结构体 - 数据类型 -初始化事件结构体 */
        env.events=EPOLLIN;//修改文件描述符状态为 准备读 - 事件状态 - 非ready态
        env.data.fd=fd;//文件描述符 - 这里指有名管道
        /* 5 操作内核epoll事件表 - EPOLL_CTL_ADD(增-fd)-EPOLL_CTL_DEL(删-fd)-EPOLL_CTL_MOD(改-fd事件) */
        epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&env);//建立文件描述符fd及其对应的事件env,并将文件描述符fd添加到epoll事件表epfd中
    
        env.events=EPOLLIN;
        env.data.fd=0;
        epoll_ctl(epfd,EPOLL_CTL_ADD,0,&env);//终端也将发生同样的事件(EPOLLIN),所以将终端(此指stdin)也加入至该内核事件表
    
        /* 6 管道读(写) - 终端和管道-谁来数据就读谁 */
        while(1)
        {
            /* 6.1 监听内核epoll事件表,并返回ready态的文件描述符数量 */
            fready=epoll_wait(epfd,retenv,2,-1);//这里通过结构体数组retenv作为出参,将ready态的文件描述符带出-ready态
            if(-1==fready)
            {
                perror("fail to epoll_wait");
                return -1;
            }
    
            /* 6.2 遍历所有ready态的文件描述符 */
            for(i=0;i<fready;i++)
            {
                /* 通过ready态文件描述符进一步确认是哪个文件描述符状态被置位 -
                * 检测(类似于IO输入检测-key检测的思想-位掩码)-查询*/
                if(retenv[i].data.fd==0)
                {
                    memset(tmpbuff,0,sizeof(tmpbuff));
                    fgets(tmpbuff,sizeof(tmpbuff),stdin);
                    printf("stdin: %s\n",tmpbuff);
                }
                else if(retenv[i].data.fd==fd)
                {
                    memset(tmpbuff,0,sizeof(tmpbuff));
                    read(fd,tmpbuff,sizeof(tmpbuff));
                    printf("fifo: %s\n",tmpbuff);
                }
            }
        }
    
        /* 6 关闭管道*/
        close(fd);
    }
    

    1.2 运行结果

    1.3 分析总结

  • 相关阅读:
    数独的解法
    LXC、Docker、 Kubernetes 容器以及Hypervisor的区别
    vim基本操作
    GPU架构演进十年,从费米到安培
    C++ 11 内敛函数inline
    【STM32】定时器与PWM的LED控制
    使用docker安装redis并持久化
    Java#19(面向对象三大特征之一:多态)
    linux下创建文件夹软链接
    博弈论学习笔记(1)——知识要点回顾(自用)
  • 原文地址:https://blog.csdn.net/qq_63713157/article/details/139418159