io多路复用监听有名管道的阻塞问题

io多路复用监听有名管道的阻塞问题

尝试封装了一下,poll和有名管道读取的代码。
读取端使用O_RDONLY|O_NONBLOCK打开,写端是 只写 打开管道

问题:写端是处于阻塞状态,读端一直循环打印timout的情况,没有读取到管道数据图片描述

代码:
1.fifo.h

#ifndef __FIFO_H__
#define __FIFO_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>

#define FIFO_NAME "./fifo"

extern int readfifo(char *, int );
extern int writefifo(char *, int );

#endif

2.fifo.c

#include "fifo.h"

// 从管道读取数据
int readfifo(char *buffer, int sizebuff)
{
    ssize_t rbytes;
    int fd = 0;

    fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK); // 打开管道文件, _oflag: 非阻塞只读

    if (fd == -1)
    {
        perror("[ERROR] open():");
        exit(EXIT_FAILURE);
    }

    rbytes = read(fd, buffer, sizebuff); // 从管道文件读数据

    if (rbytes == -1)
    {
        perror("read():");
        return -1;
    }

    close(fd);

    return 0;
}

// 向管道发送数据
int writefifo(char *buffer, int sizebuff)
{
    int fd = 0;  // 文件描述符
    int ret = 0; // 函数返回值
    char _buffer[sizebuff];
    ssize_t wbytes;

    strcpy(_buffer, buffer);

    /**
     int access(const char *pathname,int mode)
参数:
     pathname:表示要测试的文件的路径
     mode:表示测试的模式可能的值有:
     R_OK:是否具有读权限
     W_OK:是否具有可写权限
     X_OK:是否具有可执行权限
     F_OK:文件是否存在

返回值:
     若测试成功则返回0,否则返回-1

    */
    ret = access(FIFO_NAME, F_OK); // 判断有名管道文件是否存在

    if (ret == -1)
        mkfifo(FIFO_NAME, 0644); // 创建管道文件

    fd = open(FIFO_NAME, O_WRONLY); // 打开管道文件

    if (fd == -1)
    {
        perror("[ERROR] open():");
        exit(EXIT_FAILURE);
    }

    wbytes = write(fd, buffer, strlen(buffer) - 1);

    if (wbytes == -1)
    {
        perror("write():");
        return -1;
    }

    close(fd);

    return 0;
}

3.io_poll.c

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#include "fifo.h"

#define NFDS_POLLIN 1

struct pollfd *init_pollfd(int fdsarr[], nfds_t nfds, short int events)
{
    struct pollfd *pfds = (struct pollfd *)calloc(nfds, sizeof(struct pollfd));

    if (NULL == pfds)
    {
        fprintf(stdout, "init pollfd() faile.\n");
        fflush(NULL);
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < nfds; i++)
    {
        pfds[i].fd = fdsarr[i];
        pfds[i].events = events;
    }

    return pfds;
}

int main(char argc, char *argv[])
{
    int ret, i = 0;
    char buffer[64] = {0};
    int data_size = sizeof(buffer);
    int fds_pollin[NFDS_POLLIN] = {0};

    // 带参数数据发送端
    if (argc == 2)
    {
        writefifo(argv[1], data_size);
        fprintf(stdout, "wirte [ %s ] in fifo allready. ", argv[1]);
        fflush(NULL);

        return 0;
    }

    // 无参数据接收端
    struct pollfd *pfds = init_pollfd(fds_pollin, NFDS_POLLIN, POLLIN);

    for (;;)
    {
        ret = poll(pfds, NFDS_POLLIN, 1000);
        if (ret == -1)
        {
            perror("[ERROR] poll(): ");
            exit(EXIT_FAILURE);
        }
        else if (ret == 0)
        {
            printf("Timeout.\n");
        }
        else if (ret > 0)
        {
            for (i = 0; i < NFDS_POLLIN; i++)
            {
                // 判断返回的就绪事件是否为 POLLIN
                if (pfds[i].revents & POLLIN)
                {
                    readfifo(buffer, data_size); // 读取管道数据
                    printf("buffer :  %s ", buffer);
                }
            }
        }
    }

    free(pfds);

    return 0;
}

正在回答 回答被采纳积分+1

登陆购买课程后可参与讨论,去登陆

2回答
慕小白0101 提问者 2023-05-30 15:16:39

老师,请问我在使用select练习中为啥会出现perror打印 select(): Bad file descriptor

https://img1.sycdn.imooc.com//climg/6475a2420931825211970202.jpg

select() 练习代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>

#define FIFO_NAME "./fifo"

int openfifo()
{
    int fd;

    fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK); // 打开管道文件, _oflag: 非阻塞只读
    if (fd == -1)
    {
        perror("[ERROR] open():");
        exit(EXIT_FAILURE);
    }

    return fd;
}

/** 从管道读取数据
 @param buffer 将有名管道中的数据写入buffer中
 @param sizebuff 有名管道中sizebuff长度的数据写入到buffer中

*/
int readfifo(int fd, char *buffer, int sizebuff)
{
    ssize_t rbytes;

    rbytes = read(fd, buffer, sizebuff); // 从管道文件读数据

    if (rbytes == -1)
    {
        perror("read():");
        return -1;
    }

    close(fd);

    return 0;
}

/** 向管道发送数据
 @param buffer 将buffer中的数据写入有名管道中
 @param sizebuff 写入sizebuff长度的数据到有名管道中

*/
int writefifo(char *buffer, int sizebuff)
{
    int fd = 0;     // 文件描述符
    int ret = 0;    // 函数返回值
    ssize_t wbytes; // 写操作的返回值

    /**
     int access(const char *pathname,int mode)
参数:
     pathname:表示要测试的文件的路径
     mode:表示测试的模式可能的值有:
     R_OK:是否具有读权限
     W_OK:是否具有可写权限
     X_OK:是否具有可执行权限
     F_OK:文件是否存在

返回值:
     若测试成功则返回0,否则返回-1

    */
    ret = access(FIFO_NAME, F_OK); // 判断有名管道文件是否存在

    if (ret == -1)
        mkfifo(FIFO_NAME, 0644); // 创建管道文件

    fd = open(FIFO_NAME, O_WRONLY); // 打开管道文件

    if (fd == -1)
    {
        perror("[ERROR] open():");
        exit(EXIT_FAILURE);
    }

    wbytes = write(fd, buffer, sizebuff);

    if (wbytes == -1)
    {
        perror("write():");
        return -1;
    }

    close(fd);

    return 0;
}
int main(char argc, char *argv[])
{
    int ret = 0;
    int maxfd = 0;
    fd_set readfds, tmpfds;
    struct timeval tv = {3, 0}, tmp_tv;

    char buffer[64] = {0};
    int data_size = sizeof(buffer);

    // 带参数数据发送端
    if (argc == 2)
    {
        writefifo(argv[1], data_size);
        fprintf(stdout, "wirte [ %s ] in fifo allready. \n", argv[1]);
        fflush(NULL);

        return 0;
    }

    int fd = openfifo();
    maxfd = fd;
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);

    for (;;)
    {
        tmp_tv = tv;
        tmpfds = readfds;

        ret = select(maxfd + 1, &tmpfds, NULL, NULL, &tmp_tv);
        if (ret == -1)
        {

            perror("[ERROR] select(): ");
            exit(EXIT_FAILURE);
        }
        else if (ret == 0)
        { //  超时返回

            printf("Timeout.\n");
        }
        else if (ret > 0)
        {

            if (FD_ISSET(fd, &tmpfds))
            { // 判断是否在集合中

                readfifo(fd, buffer, data_size);
                fprintf(stdout, "buffer : %s \n", buffer);
                fflush(NULL);
            }
        }
    }

    return 0;
}


  • 是因为你在读取一次管道数据后,已经将读端的写文件描述符关闭了, 然后下一次 select 在继续监控的时候,发现这个文件描述符是已经关闭的,所以就会报错

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/select.h>
     
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <unistd.h>
     
    #define FIFO_NAME "./fifo"
     
    int openfifo()
    {
        int fd;
     
        fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK); // 打开管道文件, _oflag: 非阻塞只读
        if (fd == -1)
        {
            perror("[ERROR] open():");
            exit(EXIT_FAILURE);
        }
     
        return fd;
    }
     
    /** 从管道读取数据
     @param buffer 将有名管道中的数据写入buffer中
     @param sizebuff 有名管道中sizebuff长度的数据写入到buffer中
     
    */
    int readfifo(int fd, char *buffer, int sizebuff)
    {
        ssize_t rbytes = 0;
     
        rbytes = read(fd, buffer, sizebuff); // 从管道文件读数据
     
        if (rbytes == -1)
        {
            perror("read():");
            return -1;
        }
    	else 
    		return rbytes;
     
        //close(fd);
     
        return 0;
    }
     
    /** 向管道发送数据
     @param buffer 将buffer中的数据写入有名管道中
     @param sizebuff 写入sizebuff长度的数据到有名管道中
     
    */
    int writefifo(char *buffer, int sizebuff)
    {
        int fd = 0;     // 文件描述符
        int ret = 0;    // 函数返回值
        ssize_t wbytes; // 写操作的返回值
     
        /**
         int access(const char *pathname,int mode)
    参数:
         pathname:表示要测试的文件的路径
         mode:表示测试的模式可能的值有:
         R_OK:是否具有读权限
         W_OK:是否具有可写权限
         X_OK:是否具有可执行权限
         F_OK:文件是否存在
     
    返回值:
         若测试成功则返回0,否则返回-1
     
        */
        ret = access(FIFO_NAME, F_OK); // 判断有名管道文件是否存在
     
        if (ret == -1)
            mkfifo(FIFO_NAME, 0644); // 创建管道文件
     
        fd = open(FIFO_NAME, O_WRONLY); // 打开管道文件
     
        if (fd == -1)
        {
            perror("[ERROR] open():");
            exit(EXIT_FAILURE);
        }
     
        wbytes = write(fd, buffer, sizebuff);
     
        if (wbytes == -1)
        {
            perror("write():");
            return -1;
        }
    	
        close(fd);
     
        return 0;
    }
    int main(char argc, char *argv[])
    {
        int ret = 0;
        int maxfd = 0;
        fd_set readfds, tmpfds;
        struct timeval tv = {3, 0}, tmp_tv;
     
        char buffer[64] = {0};
        int data_size = sizeof(buffer);
     
        // 带参数数据发送端
        if (argc == 2)
        {
            writefifo(argv[1], data_size);
            fprintf(stdout, "wirte [ %s ] in fifo allready. \n", argv[1]);
            fflush(NULL);
     
            return 0;
        }
    
    
        int fd = openfifo();
    
    
        maxfd = fd;
    
    
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
    
     
        for (;;)
        {
            tmp_tv = tv;
            tmpfds = readfds;
    		 
            ret = select(maxfd + 1, &tmpfds, NULL, NULL, &tmp_tv);
            if (ret == -1)
            {
     			perror("[ERROR] select(): ");
                exit(EXIT_FAILURE);
            }
            else if (ret == 0)
            { //  超时返回
    		  printf("Timeout.\n");
            }
            else if (ret > 0)
            {
     
                if (FD_ISSET(fd, &tmpfds))
                { 
    				// 判断是否在集合中
    				int rret;	
                    rret = readfifo(fd, buffer, data_size);
    				if (rret != 0)
    					fprintf(stdout, "buffer : %s \n", buffer);
    					//fflush(NULL);
                }
            }
        }
     
        return 0;
    }


    2023-06-04 09:06:01
无__名 2023-05-29 17:49:57

主要原因为读进程的文件描述符没有加入到poll 监听中   int fds_pollin[NFDS_POLLIN] = {0}; 初始化为0了,没有将打开管道的文件描述符加进入

这里可以进行如下修改

extern int openfifo(const char *pathname);
extern int readfifo(int fd,char *, int );
extern int writefifo(char *, int );
// 添加了一个函数
int openfifo(const char *pathname)
{
	int fd;

	fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK); // 打开管道文件, _oflag: 非阻塞只读
	if (fd == -1)
	{
		return -1;
	}
	return fd;
}




// 从管道读取数据
int readfifo(int fd,char *buffer, int sizebuff)
{
    ssize_t rbytes;
#if 0
    int fd = 0;
    fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK); // 打开管道文件, _oflag: 非阻塞只读

    if (fd == -1)
    {
        perror("[ERROR] open():");
        exit(EXIT_FAILURE);
    }

#endif

    rbytes = read(fd, buffer, sizebuff); // 从管道文件读数据

    if (rbytes == -1)
    {
        perror("read():");
        return -1;
    }

    close(fd);

    return 0;
}



io_poll.c
include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#include "fifo.h"

#define NFDS_POLLIN 1

struct pollfd *init_pollfd(int fdsarr[], nfds_t nfds,short int events)
{
    struct pollfd *pfds = (struct pollfd *)calloc(nfds, sizeof(struct pollfd));

    if (NULL == pfds)
    {
        fprintf(stdout, "init pollfd() faile.\n");
        fflush(NULL);
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < nfds; i++)
    {
        pfds[i].fd = fdsarr[i];
        pfds[i].events = events;
    }
		
    return pfds;
}

int main(char argc, char *argv[])
{
    int ret, i = 0;
    char buffer[64] = {0};
    int data_size = sizeof(buffer);
    int fds_pollin[NFDS_POLLIN] = {0};

    // 带参数数据发送端
    if (argc == 2)
    {
        writefifo(argv[1], data_size);
        fprintf(stdout, "wirte [ %s ] in fifo allready. ", argv[1]);
        fflush(NULL);

        return 0;
    }
	
	int fd;

	fd = openfifo(FIFO_NAME);//获取读进程打开的文件
	


	fds_pollin[0] = fd; //存储到文件描述符数组中

    // 无参数据接收端
	struct pollfd *pfds = init_pollfd(fds_pollin, NFDS_POLLIN, POLLIN);

    for (;;)
    {
        ret = poll(pfds, NFDS_POLLIN, 1000);
        if (ret == -1)
        {
            perror("[ERROR] poll(): ");
            exit(EXIT_FAILURE);
        }
        else if (ret == 0)
        {
            printf("Timeout.\n");
        }
        else if (ret > 0)
        {
            for (i = 0; i < ret; i++)
            {
                // 判断返回的就绪事件是否为 POLLIN
                if (pfds[i].revents & POLLIN)
                {
                    readfifo(fd,buffer, data_size); // 读取管道数据
                    printf("buffer :  %s ", buffer);
					exit(0)	;
                }
            }
        }
    }

    free(pfds);

    return 0;
}
  • 提问者 慕小白0101 #1

    老师,你在循环判断pfds[]数组时,for循环条件是i < ret, 假如poll()函数返回值 比pfds[]数组元素个数少的话,数组靠后的元素就一直被跳过遍历了

    else if (ret > 0)
            {
                for (i = 0; i < ret; i++)
                {
                    // 判断返回的就绪事件是否为 POLLIN
                    if (pfds[i].revents & POLLIN)
                    {
                        readfifo(fd,buffer, data_size); // 读取管道数据
                        printf("buffer :  %s ", buffer);
                        exit(0) ;
                    }
                }
            }



    2023-05-29 18:18:09
  • 提问者 慕小白0101 #2

    老师,为啥你在这边添加了个exit(0) ; 读端就能接收到数据了。

    我原先不写exit(0) ;读端的程序直接停住了,也不打印timeout了

    else if (ret > 0)
            {
                for (i = 0; i < ret; i++)
                {
                    // 判断返回的就绪事件是否为 POLLIN
                    if (pfds[i].revents & POLLIN)
                    {
                        readfifo(fd,buffer, data_size); // 读取管道数据
                        printf("buffer :  %s ", buffer);
                        exit(0) ;
                    }
                }
            }


    2023-05-29 22:40:55
  • 提问者 慕小白0101 #3

    懂了,按照返回值的数量,去遍历刷新后的就绪事件

    2023-05-31 11:49:26
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师