linux epoll/select使用区分和实例对比


Linux内核poll,ppoll,epoll,select代码位置:

poll,ppoll,select相关内核实现在在fs/select.c中;

epoll_ctl和epoll_wait相关函数在fs/eventpoll.c中

epoll实测不支持监听普通文件,select可以,但是发现无论是可读、可写,其一直会返回准备就绪状态,所以select也无法实际用来监控普通文件,可用来监听物理设备如uart对应的ttyS0等。

epoll_ctl控制普通文件会返回-1,即EPERM,添加的fd不支持epoll。比如添加的是普通文件描述符内核设备文件poll file operations定义类似如下:

7205909642d848d2977fb361489ab5cb.png

如下通过epoll实现监控IO变化:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <signal.h>
#include <semaphore.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <regex.h>
#include <libgen.h>
#include <poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdarg.h>
#include <math.h>
#include <getopt.h>
#include <ctype.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/syscall.h>
#include <sys/eventfd.h>
#include <sys/socket.h>
#include <sys/reboot.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/un.h>
#include <sys/shm.h>
#include <stddef.h>

#include <linux/input.h>
#include <linux/types.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <linux/netlink.h>


static bool thread_start = 0;
static int32_t epoll_fd  = -1;
static int32_t g_test_gpio_fd = -1;
static int32_t g_test_status_fd = -1;
static int test_action  = 1;

typedef struct{
    int8_t              thread_name[OWN_THREAD_NAME_SIZE];     //thread name
    pthread_t     m_thead;                                //thread hanle
    uint32_t            thread_priority;                        //thread priority
    uint32_t            stack_depth;                            //stack size
    void                *thread_func;                           //thread func
    void                *para;                                  
}OWN_thread_t;

#define OWN_THREAD_NAME_SIZE      20
#define TEST_PATH      "/etc/test_value"
#define OWN_WAKE_GPIO_MAX_SIZE             (10)
#define OWN_MIN_STACK         100*1024
#define F_FAIL -1
#define F_SUCCESS 1
#define EPOLL_TIMEOUT (1000)
#define OWN_BUFSIZE_4                  4
#define OWN_THREAD_PRIORITY            3
#define OWN_test_THREAD_STACK_SIZE       (18 * 1024)//byte(18kb)

int32_t OWN_thread_create(OWN_thread_t *threadin)
{
    int32_t ret = -1;

    if(threadin == NULL)
    {
        printf("threadin is null\n");
    }
        pthread_attr_t attr;
        ret = pthread_attr_init(&attr);
        if (ret != 0)
        {
           printf("pthread_attr_init Error\n");
           return -1;
        }
        if(threadin->stack_depth < OWN_MIN_STACK)
        {
            threadin->stack_depth = OWN_MIN_STACK;
        }
        ret = pthread_attr_setstacksize(&attr, threadin->stack_depth);
        if(ret != 0)
        {
           printf("pthread_attr_setstacksize %d Error=%d\n",threadin->stack_depth,ret);
           return -1;
        }
        ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        if(ret != 0)
        {
           printf("pthread_attr_setdetachstate Error=%d\n",ret);
           return -1;
        }
        ret = pthread_create(&(threadin->m_thead), &attr, threadin->thread_func, (void *)(threadin->para));
        pthread_attr_destroy (&attr);
        if(ret != 0)
        {
            printf("pthread_create Error, ret=%d, err:%s\n", ret, strerror(errno));
            return F_FAIL;
        }
        else
        {
            return F_SUCCESS;
        }

    return F_FAIL;
}

static void *OWN_thread_handler(void *arg)
{
    static int32_t first_reload = 1;
    int32_t fd_cnt = 0;
    int32_t ret = 0;
    int32_t i = 0;
    int8_t value[OWN_BUFSIZE_4] = {0};
    struct epoll_event events[OWN_WAKE_GPIO_MAX_SIZE];
    int8_t value_status = 0;

    while (thread_start)
    {
        memset(events, 0x0, sizeof(events));
        fd_cnt = epoll_wait(epoll_fd, events, OWN_WAKE_GPIO_MAX_SIZE, EPOLL_TIMEOUT);
        if(fd_cnt <= 0)
        {
            continue;
        }

        for(i = 0; i < fd_cnt; i++)
        {
            memset(value, 0x0, sizeof(value));
            lseek(events[i].data.fd, 0, SEEK_SET);
            ret = read(events[i].data.fd, value, sizeof(value));
            value_status = atoi((const char *)value);
            printf("value =%s value_status =%d ret : %d\n", value, value_status, ret);

            if (events[i].events & EPOLLPRI  && events[i].data.fd == g_test_gpio_fd)
            {
                printf("gpio test file \r\n");
                if (test_action != NULL)
                {

                    printf("callback test value =%s value_status =%d ret : %d\n", value, value_status, ret);
                }
            }
        }
    }

    return NULL;
}

int main()
{
    int ret = 0;

    struct epoll_event evd = {0};
    OWN_thread_t test_gpio_thread;
    epoll_fd = epoll_create(OWN_WAKE_GPIO_MAX_SIZE);
    if (epoll_fd < 0)
    {
        printf("epoll error \n");
    }
    g_test_gpio_fd = open(TEST_PATH , O_RDONLY | O_NONBLOCK);
    if (g_test_gpio_fd < 0)
    {
        printf("open error \n");
    }

    memset(&evd, 0x0, sizeof(evd));
    evd.data.fd = g_test_gpio_fd;
    evd.events = EPOLLPRI;

    ret = epoll_ctl(g_test_epoll_fd, EPOLL_CTL_ADD, epoll_fd, &evd);
    if (ret < 0)
    {
        printf("epoll_ctl error \n");
    }

    thread_start = 1;
    memset(&test_gpio_thread, 0, sizeof(test_gpio_thread));
    snprintf((char*)test_gpio_thread.thread_name,OWN_THREAD_NAME_SIZE - 1, "Notify handle");
    test_gpio_thread.thread_priority  = OWN_THREAD_PRIORITY;
    test_gpio_thread.stack_depth      = OWN_test_THREAD_STACK_SIZE;
    test_gpio_thread.thread_func      = OWN_thread_handler;
    test_gpio_thread.para             = NULL;

    ret = OWN_thread_create(&test_gpio_thread);
    if (ret != F_SUCCESS) {
        printf("OWN_thread_handler creation failure !\n");
    }
    while(1)
    {
        sleep(2);
    }

    return 0;
}

select应用示例:如下实例为监听普通文件,发现返回值一直大于0,监听意义不大。可以将监听的设备改为ttyS0,则可以正常监控。

函数返回值如下:

>0:就绪描述字的正数目

-1:出错

0 :超时

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <signal.h>
#include <semaphore.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <regex.h>
#include <libgen.h>
#include <poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdarg.h>
#include <math.h>
#include <getopt.h>
#include <ctype.h>


#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/syscall.h>
#include <sys/eventfd.h>
#include <sys/socket.h>
#include <sys/reboot.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/un.h>
#include <sys/shm.h>
#include <stddef.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <linux/netlink.h>

static pthread_t test_thread;
int fd = 0;
static void *thread_func(void * arg);

int main(int argc, char * argv [ ])
{
    int ret;
    char buf[] = "hello test! ";

    if (argc < 2)
    {
        return -1;
    }

    setbuf(stdout, NULL);

    fd = open("/etc_rw/test_select", O_RDWR  | O_NONBLOCK);
    if (fd > 0)
    {
        printf("Open success! \n");
    }
    else
    {
        printf("Open failure! \n");
    }

    if (pthread_create(&test_thread, NULL, thread_func, NULL) != 0)
    {
        printf("Failed to creat thread \n");
    }

    while (fd >= 0)
    {
        //ret = write(fd, (uint8_t*)buf, strlen(buf));
        printf("Write fd value = %d, ret value = %d \n", fd, ret);
        sleep(2);
    }

    return 0;
}

static void *thread_func(void * arg)
{
    int ret;
    fd_set fdset;
    struct timeval timeout = {4, 0};
    char buf[100] = {0};

    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    while (fd >= 0)
    {
        FD_ZERO(&fdset);
        FD_SET(fd, &fdset);
        ret = select(fd + 1, &fdset, NULL, NULL, &timeout);
        if (ret == -1)
        {
            printf("Failed to select \n");
        }
        else if (ret == 0)
        {
            printf("ret value is 0 \n");
            timeout.tv_sec = 4;
            timeout.tv_usec = 0;
        }
        else
        {
            if (FD_ISSET(fd, &fdset))
            {
                //do
               // {
                    memset(buf, 0, 100);
                    ret = read(fd, buf, 100);
                    printf("Read ret = %d buf = %s \n", ret, buf);
               // }while(ret == 200);
            }
        }
    }

    return (void *)1;
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下! Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 先说明两个结构体: 第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 具体解释select的参数: int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 返回值: 负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a2591748032-随心所记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值