本序列涉及的 Linux 源码都是基于 linux-4.14.143 。

1. 文件抽象 与 poll 操作

1.1 文件抽象

在 Linux 内核里,文件是一个抽象,设备是个文件,网络套接字也是个文件。

文件抽象必须支持的能力定义在 file_operations 结构体里。

在 Linux 里,一个打开的文件对应一个文件描述符 file descriptor/FD,FD 其实是一个整数,内核把进程打开的文件维护在一个数组里,FD 对应的是数组的下标。

文件抽象的能力定义:

// 源码位置:include/linux/fs.h

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);

ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);

int (*iterate) (struct file *, struct dir_context *);

int (*iterate_shared) (struct file *, struct dir_context *);

// 对于 select/poll/epoll 最重要的实现基础

// 非阻塞的轮询文件状态的函数

unsigned int (*poll) (struct file *, struct poll_table_struct *);

// 省略其他函数指针

} __randomize_layout;

// 源码位置:include/linux/poll.h

typedef struct poll_table_struct {

// 文件的 file_operations.poll 实现一定会调用的队列处理函数

poll_queue_proc _qproc;

// poll 操作敢兴趣的事件

unsigned long _key;

} poll_table;

// poll 队列处理函数

typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);

1.2 文件 poll 操作

poll 函数的原型:

unsigned int (*poll) (struct file *, poll_table *);

/**

* 如果 poll_table 有回调函数,则回调它。

*

* @filp 要监听的目标文件

* @wait_address 要监听事件的等待队列头

* @p select/poll/epoll 调用里传入里的等待节点

*/

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

{

if (p && p->_qproc && wait_address)

p->_qproc(filp, wait_address, p);

}

文件抽象 poll 函数的具体实现必须完成两件事(这两点算是规范了):

1. 在 poll 函数敢兴趣的等待队列上调用 poll_wait 函数,以接收到唤醒;具体的实现必须把 poll_table 类型的参数作为透明对象来使用,不需要知道它的具体结构。

2. 返回比特掩码,表示当前可立即执行而不会阻塞的操作。

unsigned int scull_p_poll(struct file *filp, poll_table *wait)

{

Scull_Pipe *dev = filp->private_data;

unsigned int mask = 0;

/*

* The buffer is circular; it is considered full

* if "wp" is right behind "rp". "left" is 0 if the

* buffer is empty, and it is "1" if it is completely full.

*/

int left = (dev->rp + dev->buffersize - dev->wp) % dev->buffersize;

// 在不同的等待队列上调用 poll_wait 函数

poll_wait(filp, &dev->inq, wait);

poll_wait(filp, &dev->outq, wait);

/* readable */

if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM;

/* writable */

if (left != 1) mask |= POLLOUT | POLLWRNORM;

return mask;

}

2. poll 的等待与唤醒

poll 函数接收的 poll_table 只有一个队列处理函数 _qproc 和感兴趣的事件属性 _key。

文件抽象的具体实现在构建时会初始化一个或多个 wait_queue_head_t 类型的事件等待队列 。

poll 等待的过程:

poll 函数被调用时,其实现肯定会调用 poll_wait,进而调用到 _qproc 函数。

_qproc 负责构建包含 wait_queue_entry 结构体的等待节点(比如 select 操作是 poll_table_entry 结构体),并把 wait_queue_entry 添加到要监听文件的等待队列 wait_address 上(wait_queue_entry 结构体指定了事件发生时的唤醒函数,比如 select 操作里指定的是 pollwake 函数)。

poll 函数返回文件当前可立即执行而不阻塞的操作表示码。

事件发生时的唤醒过程:

当事件发生时,文件的具体实现遍历等待队列,调用其唤醒函数,由唤醒函数进行具体的唤醒操作,唤醒函数的类型为 typedef int (*wait_queue_func_t)(struct wait_queue_entry *wq_entry, unsigned mode, int flags, void *key)。

具体的唤醒函数实现根据 wait_queue_entry 找到 _qproc 函数里构建的等待节点,利用其数据判断是否需要唤醒,是则唤醒等待进程。

一个小困惑:

唤醒函数是如何根据 wait_queue_entry 找到真实的等待节点呢??

这是借助内核的一个宏 container_of 实现的,container_of 是指针的一个灵活应用,作用是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

linux poll函数 实现,Linux select/poll/epoll 原理(一)实现基础相关推荐

  1. 详情讲述Linux网络编程关注的问题丨epoll原理丨reactor模型丨三次挥手丨四次握手丨多线程丨单线程丨C/C++Linux丨C++后端开发

    90分钟搞懂linux网络编程关注的问题 1. 三次挥手,四次握手 2. epoll实现原理剖析 3. reactor模型封装 单线程.多线程以及多进程 视频讲解如下,点击观看: 详情讲述Linux网 ...

  2. linux中select和epoll原理,Linux下selectpollepoll的实现原理(一)

    最近简单看了一把 linux-3.10.25 kernel中select/poll/epoll这个几个IO事件检测API的实现.此处做一些记录. 其基本的原理是相同的,流程如下 先依次调用fd对应的s ...

  3. linux 打印函数宏,linux内核中的嵌入式汇编宏函数

    在看linux内核代码时,常会遇到诸如:static inline _syscall0(int,fork)这样的函数.经查阅资料,发现该函数是嵌入式汇编宏函数. linux内核提供了7个非常有用的宏定 ...

  4. linux signal函数用法,linux信号机制之sigaction构造体浅析,signal 函数,信号捕捉.

    来自:http://hi.baidu.com/phenix_yw/blog/item/6eb4ca391d1479f23a87ce19.html 信号安装函数sigaction(int signum, ...

  5. linux iconv函数失败,Linux 编码转换 (iconv失败的解决方法)

    一开始,调用iconv_open()会返回-1,使用printf("errno=%d\n",errno)查看,返回22,说是invalid argument参数非法. 试了下,确定 ...

  6. linux注册函数机制,Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动...

    原标题:Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动 2.3 函数实现机制 2.3.1 Linux 字符设备驱动 在linux 3.5.4中,用结构体cdev描述字符设备,cde ...

  7. linux 进程函数替换,Linux使用exec函数实现进程替换的代码分享

    这篇文章主要介绍了Linux 进程替换(exec函数)实现代码的相关资料,需要的朋友可以参考下 Linux 进程替换(exec函数)实现代码# include #include #include #i ...

  8. Linux导出函数控制,linux 下仅导出指定函数的方法

    节整理资料时,发现了这个,是两年前在 LOVEUNIX 上的回帖.自己都忘了,贴在这里 做个备份. linux 也有导出文件.不用特定的扩展名,且不是在 gcc 中指定参数,而是在 ld 中. 写一个 ...

  9. linux c++ 函数效率,Linux C++程序进行性能分析工具gprof使用入门

    性能分析工具 软件的性能是软件质量的重要考察点,不论是在线服务程序还是离线程序,甚至是终端应用,性能都是用户体验的关键.这里说的性能重大的范畴来讲包括了性能和稳定性两个方面,我们在做软件测试的时候也是 ...

最新文章

  1. 武林外传辅助工具详细制作过程[第二篇:查看数据]
  2. Codeforces Round #360 E
  3. Django(part22)--创建数据对象
  4. oracle客户端 tsnping时出现TNS-03505:无法解析名称
  5. dos c语言显示符号图案,在DOS命令行窗口中显示出用各种字符拼凑出来的各种图案的实现方法,如本人头像...
  6. 【建站指南】网站搭建需要准备什么?
  7. java网吧计费系统源码_网吧计费管理系统 - WEB源码|JSP源码/Java|源代码 - 源码中国...
  8. 大数据日志分析系统-介绍
  9. 计算机网络视频信号怎么传,如何将摄像机视频信号通过NDI传输到Zoom会议软件...
  10. 你写论文时发现了哪些神网站?
  11. feedsky绑定二级域名不能更新解决方法
  12. 如何解决github的code按钮一直转下载不了
  13. DAY01.使用JAVA从国家统计局爬取2020年全国统计用区划代码和城乡划分代码(省市区数据)
  14. 弱密码验证不能连续字符(如123、abc)连续3位或3位以上、不能相同字符(如111、aaa)连续3位或3位以上
  15. 【文献笔记】NOMA+D2D模型3 (D2D group)
  16. Warning: Attempt to present ... on … which is already presenting null
  17. 陀螺仪工作原理及创新应用
  18. 实不相瞒,我的“睡后收入”是工资的5倍
  19. 3.7 Dictionary(字典)
  20. springboot整合阿里云ocr对身份证或通用文字进行识别提取

热门文章

  1. php curl如何解决分页,一段PHP的分页程序,报错,该如何解决
  2. 最大似然估计_R初等统计分析(一)——概率分布、最大似然估计
  3. 填词游戏java_第八届蓝桥杯国赛 Java B组 第五题 填字母游戏(博弈论)
  4. Linux下Wireshark的Lua: Error during loading 和 couldn't run /usr/bin/dumpcap in child process 的解决方案
  5. JAVA后端常用框架SSM,redis,dubbo等
  6. Navicat for mysql 远程连接 mySql数据库10061错误问题
  7. 数字的补数——力扣476
  8. 什么叫做java程序中的继承_【Java】基础16:什么叫继承?
  9. ga算法matlab,matlab遗传算法ga函数
  10. Linux 恢复rm -rf命令所删除的达梦数据文件