poll

  • 内核执行poll过程
  • 总代码
    • 驱动程序
    • 应用程序

内核:linux -2.6.22.6

内核执行poll过程

从代码的角度来看,poll机制是通过应用程序调用poll() 函数,poll()函数的使用方法可以通过man poll进行查看,可仿照最终程序中进行改写,这里不做重复说明。配合驱动函数中相对应的drv_poll() 函数配套使用的。函数执行的入口当然是应用程序中的poll()函数,接着进入内核中的sys_poll() 函数,poll机制的函数在路经:linux/fs/select.c下,原代码如下:

739 asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,
740             long timeout_msecs)
741 {
742     s64 timeout_jiffies;
743
744     if (timeout_msecs > 0) {745 #if HZ > 1000
746         /* We can only overflow if HZ > 1000 */
747         if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ)
748             timeout_jiffies = -1;
749         else
750 #endif
751             timeout_jiffies = msecs_to_jiffies(timeout_msecs);
752     } else {753         /* Infinite (< 0) or no (0) timeout */
754         timeout_jiffies = timeout_msecs;
755     }
756
757     return do_sys_poll(ufds, nfds, &timeout_jiffies);
758 }

可以看出除了return中使用的do_sys_poll()函数,其他全都是初始化时间。所以进一步对**do_sys_poll()**函数进行分析,部分原代码如下:

653 int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout){....
670     poll_initwait(&table);                          // 初始化table
......
709     fdcount = do_poll(nfds, head, &table, timeout);710          // 真正执行poll函数
......
737 }

首先初始化table通过阅读代码可以看出table主要设置的是将table->qproc设置成__pollwait模式。
另一步骤进入do_poll函数,do_poll()函数原代码如下:

582 static int do_poll(unsigned int nfds,  struct poll_list *list,
583            struct poll_wqueues *wait, s64 *timeout)
584 {585     int count = 0;
586     poll_table* pt = &wait->pt;
587
588     /* Optimise the no-wait case */
589     if (!(*timeout))
590         pt = NULL;
591
592     for (;;) {593         struct poll_list *walk;
594         long __timeout;
595
596         set_current_state(TASK_INTERRUPTIBLE);
597         for (walk = list; walk != NULL; walk = walk->next) {598             struct pollfd * pfd, * pfd_end;
599
600             pfd = walk->entries;
601             pfd_end = pfd + walk->len;
602             for (; pfd != pfd_end; pfd++) {603                 /*
604                  * Fish for events. If we found one, record it
605                  * and kill the poll_table, so we don't
606                  * needlessly register any other waiters after
607                  * this. They'll get immediately deregistered
608                  * when we break out and return.
609                  */
610                 if (do_pollfd(pfd, pt)) {
611                     count++;
612                     pt = NULL;
613                 }
614             }
615         }
616         /*
617          * All waiters have already been registered, so don't provide
618          * a poll_table to them on the next loop iteration.
619          */
620         pt = NULL;
621         if (count || !*timeout || signal_pending(current))
622             break;
623         count = wait->error;
624         if (count)
625             break;
626
627         if (*timeout < 0) {628             /* Wait indefinitely */
629             __timeout = MAX_SCHEDULE_TIMEOUT;
630         } else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) {631             /*
632              * Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in
633              * a loop
634              */
635             __timeout = MAX_SCHEDULE_TIMEOUT - 1;
636             *timeout -= __timeout;
637         } else {638             __timeout = *timeout;
639             *timeout = 0;
640         }
641
642         __timeout = schedule_timeout(__timeout);
643         if (*timeout >= 0)
644             *timeout += __timeout;
645     }
646     __set_current_state(TASK_RUNNING);
647     return count;
648 }

通过分析将其简写为:

for(;;){ if(do_pollfd(pfd, pt)){   // do_pollfd调用我们的drv_poll函数,以及挂载队列,此函数在接下去分析count++;   //如果由我们的drv_poll函数返回值为1,时间发生pt = NULL;}if(count || *timeout || signal_pending(current))  //计数count不为0 或者 超时 或者 有信号在等待处理break;   //不休眠_timeout = schedule_timeout(__timeout);  // 休眠
}

接着分析do_pollfd函数,函数原代码如下所示:

555 static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
556 {
557     unsigned int mask;
558     int fd;
559
560     mask = 0;
561     fd = pollfd->fd;
562     if (fd >= 0) {563         int fput_needed;
564         struct file * file;
565
566         file = fget_light(fd, &fput_needed);
567         mask = POLLNVAL;
568         if (file != NULL) {569             mask = DEFAULT_POLLMASK;
570             if (file->f_op && file->f_op->poll)
571                 mask = file->f_op->poll(file, pwait);      //执行驱动函数中的drv_poll函数
572             /* Mask out unneeded events. */
573             mask &= pollfd->events | POLLERR | POLLHUP;
574             fput_light(file, fput_needed);
575         }
576     }
577     pollfd->revents = mask;
578
579     return mask;    //返回mask
580 }

由do_poll函数中的分析可以大致看出来do_pollfd函数的作用就是在判断某一条件是否满足,来使count++。所以进一步就涉及到了drv_poll的返回值mask。
接着编写驱动函数中的drv_poll函数

unsigned int botton_drv_poll(struct file *fd, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(fd, &button_waitq, wait); // 不会立即休眠  只是加入等待队列if (ev_press) //若发生中断 返回值mask = 1mask |= POLLIN | POLLRDNORM;return mask;  //若不发生中断返回值 mask = 0
}

其中主要函数为poll_wait,这个函数主要干的事情是:把当前进程添加到wait参数指定的等待列表(button_waitq)中。需要注意的是这个函数是不会引起阻塞的,真正引起休眠的是do_poll中的schedule_timeout。
所以该函数主要完成的是判断是否发生中断。若发生中断返回1,未发生中断的话返回0。
结合sys_poll()函数可以看出来,若返回值为1则发生中断唤醒,若返回值为0则为超时唤醒。
总的来说,poll加上之前的中断函数,休眠的条件有两个:

  1. read()中的休眠
  2. poll()中的休眠

但是值得注意的是,进入read的条件为:由于中断被唤醒,所以进入休眠的条件不得到满足(ev_press =1),所以总的来说只有一个中断起作用,相对应的唤醒有两个方式:

  1. 超时
  2. 有中断发生

这两种中断对应于两个返回值,以及两种处理方式。
对于整个poll处理过程可以由一下的伪代码表示:

app: poll(fds, 1, 5000);
kernal: sys_poll()do_sys_poll(..., timeout_jiffies)poll_initwait(&table)init_poll_funcptr(&pwq->pt, __pollwait)table->qproc = __pollwait   // 初始化do_poll(nfds, head, &table, timeout)for(;;){for(输入文件数组){   //如果任意一个满足条件count则不为0if(do_pollfd(pfd, pt)){   dopollfd()>>> mask = file->f_op->poll(file, pwait) >>>__pollwait(file, &button_waitq, p)count++;   //若驱动函数drv_poll的返回值为1条件发生pt = NULL;}}if(count || !*timeout || signal_pending(current))   break; // 如果发生中断或者时间超时则 不休眠__timeout = schedule_timeout(__timeout); //休眠}

因此基于poll函数功能,设计整个程序大体的流程如下图所示:

总代码

驱动程序

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/irqreturn.h>
//#include <asm/arch-s3c2410/irqs.h>
#include <linux/irq.h>
#include <asm/arch-s3c2410/regs-gpio.h>
#include <linux/poll.h>#define DEV_NAME "button_drv"  // device namestatic struct class *buttondrv_class;
static struct class_device    *buttondrv_class_dev;static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
static volatile int ev_press = 0;/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *gpgcon = NULL;
volatile unsigned long *gpgdat = NULL;struct pin_desc{volatile unsigned long *key_Register;int key_pin;int key_num;
};struct pin_desc pins_desc[4] = {{0x01, 0,  1},{0x02, 2,  2},{0x03, 3,  3},{0x04, 11, 4},
};static irqreturn_t button_irq(int irq, void *dev_id)
{struct pin_desc *pindesc = (struct pin_desc *)dev_id;key_val = ((*(pindesc -> key_Register)) & (1<<(pindesc -> key_pin))) ? 0 : 1;if(key_val){//    printk("%d", pindesc -> key_num);}printk("%d  ",key_val);printk("%d\n",pindesc->key_num);ev_press = 1;                  /* 表示中断发生了 */wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */return IRQ_HANDLED;
}static int button_drv_open(struct inode *inode, struct file *file)
{pins_desc[0].key_Register = gpfdat;pins_desc[1].key_Register = gpfdat;pins_desc[2].key_Register = gpgdat;pins_desc[3].key_Register = gpgdat;request_irq(IRQ_EINT0,  button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);request_irq(IRQ_EINT2,  button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);return 0;
}
ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{if(size == -1)return -EINVAL;    /* 如果没有按键动作, 休眠 */wait_event_interruptible(button_waitq, ev_press);/* 如果有按键动作, 返回键值 */copy_to_user(buf, &key_val, 1);ev_press = 0;return 1;
}static int EINTkey_close(struct inode *inode, struct file *file)
{// FREE IRQ free_irq(IRQ_EINT0, &pins_desc[0]);free_irq(IRQ_EINT2, &pins_desc[1]);free_irq(IRQ_EINT11, &pins_desc[2]);free_irq(IRQ_EINT19, &pins_desc[3]);return 0;
}unsigned int botton_drv_poll(struct file *fd, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(fd, &button_waitq, wait); // 不会立即休眠if (ev_press)mask |= POLLIN | POLLRDNORM;return mask;
}static struct file_operations button_drv_fops = {.owner   =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open    =   button_drv_open,     .read    =   button_drv_read,.release =   EINTkey_close,.poll    =   botton_drv_poll,
};
int major;
static int button_drv_init(void)
{major = register_chrdev(0, DEV_NAME, &button_drv_fops); // 注册, 告诉内核// register chrdev. to major (major, "drv name", file operations);buttondrv_class = class_create(THIS_MODULE, "buttondrv");if(IS_ERR(buttondrv_class)){printk( "ERROR creat key class");}buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/xyz */if(buttondrv_class_dev == NULL){printk("ERROR creat dev");}gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //remap button GPFCPNgpfdat = gpfcon + 1;                                        //remap button GPFDATgpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); //remap button GPGCPNgpgdat = gpgcon + 1;                                        //remap button GPGDATreturn 0;
}static void button_drv_exit(void)
{unregister_chrdev(major, DEV_NAME); // 卸载class_device_unregister(buttondrv_class_dev);class_destroy(buttondrv_class);iounmap(gpgcon);iounmap(gpfcon);           // unmap virtual address
}module_init(button_drv_init); //When install drv, system will find init function
module_exit(button_drv_exit);   // Uinstall drvMODULE_LICENSE("GPL");

应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>// button_drv int main(int argc, char **argv)
{int ret;   // poll useint fd;    // unsigned char button_val;fd = open("/dev/buttons", O_RDWR);if (fd < 0)printf("can't open!\n");if(argc != 1)    {printf("error parterner\n");printf("%s\n",argv[0]);return 0;}struct pollfd fds[1];fds[0].fd = fd;fds[0].events = POLLIN;while(1){ret = poll(fds, 1, 5000);if(ret == 0)printf("time out \n");elseread(fd, button_val, sizeof(button_val));}return 0;
}

S3C2440 开发板实战(9):poll机制相关推荐

  1. linux 内核驱动的poll,嵌入式Linux驱动开发(五)——poll机制原理以及驱动实现...

    前情回顾: 再开始今天的内容之前,先简单review一下,我们都用了什么方案来获取按键值,他们的特点都是什么.只有不断地理清了思路,我们才能够更好的理解,为何会出现如此多的解决方案,当遇到问题的时候, ...

  2. S3C2440 开发板实战(8):中断驱动

    中断驱动 一.查询方式 1.驱动程序 2.应用程序 3.结果测试 二.中断方式 1.中断内部函数实现方式(理论框架) 1.1. 异常处理结构 (1)设置异常向量表 (2)异常服务函数调用 1.2. 中 ...

  3. c++ linux 线程等待与唤醒_Linux驱动程序基石-POLL机制(附.视频)

    今天<升级版全系列嵌入式视频_入门篇>新增一节视频:19.2_POLL机制 时长24分钟,免费观看 何为POLL机制? 给驱动程序加一个闹钟,让APP不必死等数据: 既可以快速掌握 POL ...

  4. Linux内核poll机制分析

    1. poll 机制 本篇主要是进行 poll 内核实现,调用流程的分析. 1.1 sys_poll 分析 应用程序调用 poll() 后,经过 SWI 的处理后,最终会进入内核的 sys_poll( ...

  5. Android开发之通过接口回调机制加载数据(源代码分享)

    Android开发之通过接口回调机制加载数据的简单实现,在实际开发中通过callback方法得到网络加载的数据的使用频率远比通过直接开启线程或异步任务加载数据的频率高的多,这篇文章的代码将简单实现该机 ...

  6. linux poll in,Linux poll机制详细讲解

    所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open ...

  7. 字符设备驱动程序之poll机制

    前面的按键中断驱动,只能实现当有按键按下的时候,输出,平时cpu处于休眠状态.如果我想实现,休眠一段时间执行一些指令,当有中断发生时,cpu又可以立即响应.那就得用poll机制. poll机制分析 韦 ...

  8. 【Verilog】基于Nexys4DDR开发板实现数字钟

    功能: 基于Nexys4DDR开发板实现的数字钟,六位数码管显示时分秒,可切换24时制/12时制,有整点报时功能(led灯闪烁). Verilog代码: `timescale 1ns / 1ps//数 ...

  9. 韦东山驱动视频笔记——3.字符设备驱动程序之poll机制

    linux内核版本:linux-2.6.30.4 目的:我们在中断方式的按键应用程序中,如果没有按键按下,read就会永远在那等待,所以如果在这个程序里还想做其他事就不可能了.因此我们这次改进它,让它 ...

最新文章

  1. ST-3806系列单圈编码器 测试说明
  2. 中国电磁线行业发展前景预测与竞争态势分析报告2022-2028年版
  3. C语言 | 使用牛顿法求非线性方程的一个实根(附代码)
  4. android平台 arcgisr_第一个基于ArcGIS的Android应用
  5. easyui被activeX控件挡住的解决方法
  6. asp.net服务器端对话框控件的简单实现(附源码)
  7. html5在别的电脑上打不开,U盘在别台电脑上打不开的原因分析及解决
  8. PC微信小程序突然Charles抓不到包
  9. weblogic部署静态网页
  10. linux脚本每月1日跑一次,Linux crontab 每分钟、每小时、每天、每周、每月、每年定时执行...
  11. 机架式服务器主要内部组件,戴尔R815机架式服务器
  12. 怎么简单把word转成PDF并生成书签
  13. Python 数据分析 git 工具使用 flask学习
  14. 小型电话薄管理系统(Mysql数据库)
  15. java实现支付宝二维码支付(Spring Boot)
  16. 2022年全球与中国电缆悬挂夹市场现状及未来发展趋势
  17. C++ 快速学习(一)
  18. 【Scratch-外观模块】漩涡特效指令
  19. hadoop的journalnode节点出现Can‘t scan a pre-transactional edit log错误解决办法
  20. WIFI WPS 种类

热门文章

  1. 华为 2017 实习生招聘笔试题
  2. WINDOWS下kill进程的命令
  3. 关于pos打印机通过tcp/ip操作打印指令集
  4. 【全网最暴力解决方案】使用gdb调试时遭遇“Missing separate debuginfos, use: debuginfo-install glibc....”报错信息
  5. 结合Java和机器学习技术,如何驾驭大数据提升业务效率和竞争力?
  6. win、linux、unix查看系统主机名
  7. 如何制作淘宝主图视频
  8. 数据挖掘之关联规则挖掘的一些定义
  9. Google Play网页显示语言切换方法
  10. 第九届蓝桥杯(省赛)C++C组真题题解