select,poll源码分析

1.0版本的select已经看过了,2.6中已经完全重构了,代码每次看都好像懂了,但每次回忆核心流程又感觉有点勉强,我希望通过一种关键流程的形式去分析,而不是贴一大堆的代码

  1. 关键结构对象poll_wqueues,在每次do_select中会被new一个出来并初始化,初始化的关键是把__pollwait方法指针设置到pt中,后面会分析到poll_wait方法的时候,实际上去执行的就是这儿设置的__pollwait
/*
 * Structures and helpers for select/poll syscall
 */
struct poll_wqueues {
    poll_table pt;
    struct poll_table_page *table;  // 当inline_entries数组使用完了之后,会动态申请的页
    struct task_struct *polling_task;  // 当前等待的进程
    int triggered;  // 当被唤醒后该值被设置,以防重复唤醒
    int error;
    int inline_index;  // 当前inline_entries使用到的位置
    struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
};

// do_select中对poll_wqueues进行初始化
void poll_initwait(struct poll_wqueues *pwq)
{
    init_poll_funcptr(&pwq->pt, __pollwait);
    pwq->polling_task = current;
    pwq->triggered = 0;
    pwq->error = 0;
    pwq->table = NULL;
    pwq->inline_index = 0;
}
  1. *f_op->poll,linux使用了类似接口的模块开发,file对象只是一个接口层,每个file对象都有一个f_op对象,是实际上真正驱动程序的代码。所以,*f_op->poll就是调用不同的硬件驱动的poll方法。
    一般对应的驱动中都会有一个自己的等待队列而且是统一的,linux给等待队列定义了两个对象wait_queue_head_twait_queue_t,wait_queue_head_t是一个包含了自旋锁的双向链表,链表当然也是使用了linux中的list_head了,注意了,该链表除了表头也就是wait_queue_head_t,其他项都是wait_queue_t结构了,而wait_queue_t则是存放等待进程的真正地方。
// 真正存放等待队列中等待进程的地方
typedef struct __wait_queue wait_queue_t;
struct __wait_queue {
    unsigned int flags;
#define WQ_FLAG_EXCLUSIVE   0x01
    void *private; 
    wait_queue_func_t func;  // 唤醒方法
    struct list_head task_list;
};

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
  1. 那回到刚才说的__pollwait方法,因为最终设备的驱动都会调用poll_wait方法来触发,该方法比较重要,值得多贴一些代码来解释,该方法第一个参数是监听fd对应的file,第二个参数是驱动设备的等待队列头,第三个参数是咱们第一条说的注入进去的poll_table,所有fd都共用这一个poll_table。
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
    if (p && wait_address)
        p->qproc(filp, wait_address, p);
}

/* Add a new entry */
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
                poll_table *p)
{
    // 根据poll_table的指针取得外层对象poll_wqueues的指针
    struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
    // 先从数组中inline_entries中取空闲的,如果用完了则申请新页存放
    struct poll_table_entry *entry = poll_get_entry(pwq);
    if (!entry)
        return;
    get_file(filp);
    entry->filp = filp;
    entry->wait_address = wait_address;
    // 设置监听的类型=poll和读写
    entry->key = p->key;
    // 对wait_queue链表初始化,flags=0,唤醒func=pollwake
    init_waitqueue_func_entry(&entry->wait, pollwake);
    entry->wait.private = pwq;
    // 加入到该设备的等待队列中
    add_wait_queue(wait_address, &entry->wait);
}

这里还有个细节,一旦*f_op->poll返回的mask不为0并且是监听的类型,则会把wait也就是那个wait_table对象设置为null,这样对下一个file调用poll方法的时候就不会把自己加入到等待队列中了。

  1. 退出,当有任何一个事件可用的时候直接返回,当poll_table发生错误的时候也退出。剩下就根据超时去睡眠,方法第一次执行的时候会根据end_time设置超时expire。然后会调用poll_schedule_timeout去睡眠,如果该方法返回0代表是超时事件唤醒的则设置timeout,注意不论是超时唤醒还是被某个驱动程序的事件唤醒后,都会再走一次循环,再检查一遍每一个事件的状态。
// 第一次完毕后如果任何一个事件都没有发生,也会置wait为NULL,防止下一次循环再一次设置等待队列
wait = NULL;
// 如果有事件发生,或者timeout都会退出循环
if (retval || timed_out || signal_pending(current))
    break;
// 如果发生了错误则也退出
if (table.error) {
    retval = table.error;
    break;
}

/*
    * If this is the first loop and we have a timeout
    * given, then we convert to ktime_t and set the to
    * pointer to the expiry value.
    */
if (end_time && !to) {
    expire = timespec_to_ktime(*end_time);
    to = &expire;
}

// 开始睡眠
if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
                to, slack))
    timed_out = 1;
  1. 扫尾,从每个等待队列中删除该进程的等待项,方法是poll_freewait,就是遍历inline_entries数组,取出poll_table_entry然后挨个调用free_poll_entry方法从队列中删除。然后检查是否使用了申请的内存,有的话也要一一退出
void poll_freewait(struct poll_wqueues *pwq)
{
    struct poll_table_page * p = pwq->table;
    int i;
    // 从数组中取出poll_table_entry然后挨个调用free_poll_entry从队列释放
    for (i = 0; i < pwq->inline_index; i++)
        free_poll_entry(pwq->inline_entries + i);
    
    // 如果使用了申请的内存,则也要一一释放
    while (p) {
        struct poll_table_entry * entry;
        struct poll_table_page *old;

        entry = p->entry;
        do {
            entry--;
            free_poll_entry(entry);
        } while (entry > p->entries);
        old = p;
        p = p->next;
        free_page((unsigned long) old);
    }
}
  1. 唤醒,驱动程序可以写的时候会唤醒等待队列上的进程,调用的是wake_up_interruptible
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • select()/poll() 的内核实现 05 Jan 2015转自:http://janfan.cn/chin...
    0X7C00阅读 3,731评论 0 0
  • 预备知识 等待队列 等待队列有一个等待队列头,其他加入这个等待队列的需要加在这个头上。 需要加入等待队列的话,可以...
    梅花怒阅读 1,351评论 0 0
  • 必备的理论基础 1.操作系统作用: 隐藏丑陋复杂的硬件接口,提供良好的抽象接口。 管理调度进程,并将多个进程对硬件...
    drfung阅读 3,525评论 0 5
  • 一:base.h 二:block.h 1. dispatch_block_flags:DISPATCH_BLOCK...
    小暖风阅读 2,408评论 0 0
  • 北京的初秋,早晚天气格外凉爽。天蓝的像一面晶莹剔透的镜子,悠闲的云朵随心所欲的变幻着各种姿势,看一看天空,心里也有...
    梅开如雪阅读 699评论 7 17