4.poll接口

1.poll原理

  • poll用来做什么,就是用于在应用层select时候,驱动检查有没有数据可以读的函数。在很多函数里面都要实现。目的就是告诉应用层数据到了。

  • 当select时,是否直接返回,还得取决于传入的fd的属性,如果属性是阻塞型,会使用驱动的poll函数做检查,有数据就会返回相应的掩码,如果没有,那么select就在会阻塞,当驱动层有数据到来时,会再一次的调用poll函数做检查,如果有数据了,那么立马会唤醒阻塞的进程或者线程。那么select就会返回。如果不阻塞,当poll检查没有数据时,会立即返回错误的掩码。

  • 至于进程或者线程阻塞不是发生在驱动,而是发生在vfs层,因此在驱动的poll函数调用的poll_wait函数是没有阻塞进程的,只是将其加入一个队列而已。

  • 对于在select是这样操作的,那么read函数是怎么操作的,如果没有数据可读,那么read函数也是分为阻塞和非阻塞的情况。在阻塞的情况下,会使用
    wait_event_interruptible函数来等待条件发生。当条件发生了,驱动内的read函数会继续执行。把数据从内核层copy到用户层。非阻塞会立即返回错误。

  • 需要说明的是,poll_wait函数并不阻塞,程序中poll_wait(filp, &outq,
    wait)这句话的意思并不是说一直等待outq信号量可获得,真正的阻塞动作是上层的select/poll函数中完成的。select/poll会在一个循环中对每个需要监听的设备调用它们自己的poll支持函数以使得当前进程被加入各个设备的等待列表。若当前没有任何被监听的设备就绪,则内核进行调度(调用schedule)让出cpu进入阻塞状态,schedule返回时将再次循环检测是否有操作可以进行,如此反复;否则,若有任意一个设备就绪,select/poll都立即返回。

    2.select函数

intselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
  • (1)intmaxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错。说明:对于这个原理的解释可以看上边fd_set的详细解释,fd_set是以位图的形式来存储这些文件描述符。maxfdp也就是定义了位图中有效地位的个数。
  • (2)fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0得值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
  • (3)fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
  • (4)fd_set*errorfds同上面两个参数的意图,用来监视文件错误异常文件。
  • (5)structtimeval*
    timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第三,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即
    select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述

3.代码实例

static unsigned char queue_flag = 0; // 队列的判断条件

wait_queue_head_t read_queue; // 等待队列定义

static unsigned char read_flag = 0; // 是否有有效数据

。。。。。

static ssize_t cdev_read(struct file* filp, char __user* buf, size_t count, loff_t* f_pos)
{
    printk("wait event interruptible before\n");
    wait_event_interruptible(read_queue, queue_flag);
    printk("wait event interruptible after\n");

	if(copy_to_user(buf,  buffer, count))
	{
		return -EFAULT;
	}

    read_flag = 0;

	return count;
}


static ssize_t cdev_write(struct file* filp, const char __user* buf, size_t count, loff_t* f_pos)
{
	if(copy_from_user(buffer + *f_pos, buf, count))
	{
		return -EFAULT;
	}

    queue_flag = 1;
    read_flag = 1;
    wake_up(&read_queue);

	return count;
}


static unsigned int cdev_poll(struct file* filp, poll_table* pt)
{
    unsigned int mask = POLLIN | POLLRDNORM;

    printk("poll wait before\n");
    poll_wait(filp, &read_queue, pt);
    printk("poll wait after\n");
    
	/* 根据实际情况,标记事件类型 */
    if(read_flag == 1)
    {
        printk("have write\n");
        return mask;
    }
	
	/* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */
    printk("no write\n");
    return 0;
}

应用代码实例:

#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<linux/rtc.h>
#include<linux/ioctl.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

int fd;

void* readthread(void* arg)
{
    char data[256];
    fd_set rfds;
    fd_set wfds;
    int ret = 0;

    while(1)
    {
        FD_ZERO(&rfds);
        FD_SET(fd, &rfds);
        select(fd + 1, &rfds, &wfds, NULL, NULL);
        if(FD_ISSET(fd, &rfds))
        {
            ret = read(fd, data, 3);
            if(ret < 0)
            {
                printf("read error\n");
                return (void*)-1;
            }

            printf("data: %c %c %c\n", data[0], data[1], data[2]);
        }
    }

    return (void*)0;
}

int main(int argc, char *argv[])
{

    char data[256];
    int ret;
    pthread_t tid;

    printf("Hello World!\n");

    fd = open("/dev/chrdev", O_RDWR);
    if(fd < 0)
    {
        printf("open failed\n");
        return -1;
    }

    pthread_create(&tid, NULL, readthread, NULL);

    while(1)
    {
        ret = write(fd, "abc", 3);
        if(ret < 0)
        {
            printf("write error\n");
            close(fd);
            return -1;
        }

        sleep(20);
    }

    close(fd);

    return 0;
}

运行结果:

在这里插入图片描述

workQueue.poll是一个方法调用,用于从工作队列中获取并移除一个任务。具体来说,它会返回工作队列中的下一个任务,如果队列为空,则返回null。这个方法通常在线程池的工作线程中被调用,用于获取下一个要执行的任务。\[1\]在线程池的源码中,可以看到在执行任务之前,会先调用workQueue.poll方法来获取任务。\[2\]在线程池的实现中,每个工作线程都是通过Worker类来表示的,Worker类实现了Runnable接口,并且在构造函数中将任务赋值给firstTask成员变量。\[3\]因此,当工作线程运行时,会通过workQueue.poll方法获取下一个要执行的任务,并将其赋值给firstTask,然后调用任务的run方法来执行任务的逻辑。 #### 引用[.reference_title] - *1* *2* [异步编程学习之路(五)-线程池原理及使用,java架构师面试问题](https://blog.csdn.net/m0_65485166/article/details/122198958)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [java并发与多线程(三)--线程池原理解析](https://blog.csdn.net/qq47653423/article/details/122272131)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值