首页 > 其他分享 >select / poll 非阻塞原理

select / poll 非阻塞原理

时间:2023-02-19 22:44:55浏览次数:25  
标签:int 阻塞 ret sys fd timeout poll select

应用程序通过调用 select / poll 函数,可以实现非阻塞编程,以下举例:

int main(int argc, char *argv[])
{
    int fd;
    int ret = 0;
    char *filename;
    struct pollfd fds;
    fd_set readfds;
    struct timeval timeout;
    unsigned char data;

    if (argc != 2) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];
    fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞访问 */
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

#if 0
    /* 构造结构体 */
    fds.fd = fd;
    fds.events = POLLIN;

    while (1) {
        ret = poll(&fds, 1, 500);
        if (ret) { /* 数据有效 */
       if ( fds.revents & POLLIN ) {   ret = read(fd, &data, sizeof(data));
       } if(ret < 0) { /* 读取错误 */ } else { if(data) printf("key value = %d \r\n", data); } } else if (ret == 0) { /* 超时 */ /* 用户自定义超时处理 */ } else if (ret < 0) { /* 错误 */ /* 用户自定义错误处理 */ } } #endif while (1) { FD_ZERO(&readfds); FD_SET(fd, &readfds); /* 构造超时时间 */ timeout.tv_sec = 0; timeout.tv_usec = 500000; /* 500ms */ ret = select(fd + 1, &readfds, NULL, NULL, &timeout); switch (ret) { case 0: /* 超时 */ /* 用户自定义超时处理 */ break; case -1: /* 错误 */ /* 用户自定义错误处理 */ break; default: /* 可以读取数据 */ if(FD_ISSET(fd, &readfds)) { ret = read(fd, &data, sizeof(data)); if (ret < 0) { /* 读取错误 */ } else { if (data) printf("key value=%d\r\n", data); } } break; } } close(fd); return ret; }

 

POLL() 机制的内核代码详解

Linux APP系统调用,基本都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数。比如系统调用open、read、write、poll,与之对应的内核函数为:sys_open、sys_read、sys_write、sys_poll。 对于系统调用poll或select,它们对应的内核函数都是sys_poll。分析sys_poll,即可理解poll机制。

sys_poll函数

SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
        int, timeout_msecs)
{
    struct timespec64 end_time, *to = NULL;
    int ret;

    if (timeout_msecs >= 0) {
        to = &end_time;
        poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,
            NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
    }

    ret = do_sys_poll(ufds, nfds, to);
……

SYSCALL_DEFINE3是一个宏,它定义于include/linux/syscalls.h,展开后就是sys_poll函数。

do_sys_poll函数

int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
        struct timespec64 *end_time)
{
……
    poll_initwait(&table);
    fdcount = do_poll(head, &table, end_time);
    poll_freewait(&table);
……
}

 

 

 poll_initwait() 中把当前进程变量存在变量 pwq 内,后面会把当前进程变量作为等待队列条目的一部分加入等待队列中,用于唤醒进程。

变量 pwq 记录了 __pollwait 函数指针,此函数最后会在驱动程序的 poll() ->poll_wait() 内被执行

do_poll函数

这是POLL机制中最核心的代码

 

① 从这里开始,将会导致驱动程序的poll函数被第一次调用。

 

 沿着②③④⑤,你可以看到:驱动程序里的 poll_wait()--->__pollwait()--->add_wait_queue(),把带有当前进程变量的等待队列条目加入到驱动程序创建的等待队列中。

当调用 wake_up_interruptible(等待队列),会执行等待队列的等待队列条目的 func() 函数,即 pollwake()--->__pollwake()--->default_wake_function()--->try_to_wake_up(),从而唤醒进程。 等待队列条目对应的结构体如下:

 

当执行完①之后,在⑥或⑦处,pt->_qproc被设置为NULL,所以第二次调用驱动程序的poll时,不会再次把当前进程放入等待队列里。

⑧ 如果驱动程序的poll返回有效值,则count非0,跳出循环;

⑨ 否则休眠一段时间;当休眠时间到,或是被中断唤醒时,会再次循环、再次调用驱动程序的poll。

 

标签:int,阻塞,ret,sys,fd,timeout,poll,select
From: https://www.cnblogs.com/god-of-death/p/17135835.html

相关文章