linux内核延时操作

vmlinz posted @ Thu, 28 Apr 2011 17:30:46 +0800 in Linux with tags linux kernel , 6300 readers

linux内核延时操作

系统为了实现中断的快速处理,将中断处理程序分为处理中断请求的上半部和延时处理逻辑和I/O的下半部(buttom halves)。下半部的主要实现机制有两种:tasklet和workqueue。

tasklets

tasklet是软件中断(softirq)机制的一部分,tasklet运行在软件中断上下文中,所以有软件中断服务函数的所有要求,比如不能休眠。tasklet相对于软件中断有一些针对驱动编写的好处:

  • 通过软件中断实现,回调函数不能睡眠
  • tasklet的分配是动态的,可以在内核运行时添加和删除
  • 同类型的tasklet总是串行执行的,因为它的主要数据结构是per-cpu的,所以不需要人为使用spinlock。但是不同的tasklet则可以并发地在不同cpu上运行。
  • tasklet的串行化使tasklet函数不必是可重入的。
  • 不能访问用户态进程地址

softirq implementation

kernel/softirq.c

  • static __init int spawn_ksoftirqd(void); 创建softirq的内核线程,用于延迟处理过多的softirq,这是softirq处理速度和cpu公平性的一个折中处理。
  • kthread_create(run_ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu); 设置内核线程的回调函数run_ksoftirqd
  • static int run_ksoftirqd(void * __bind_cpu); 内核线程回调,if (local_softirq_pending()) __do_softirq();如果本地cpu有待处理的softirq,则调用softirq的处理函数
  • asmlinkage void __do_softirq(void); 根据softirq_vec对应中对应的位来调用相应的action,循环处理所有待处理的softirq,处理过程中间如果有新来的softirq则会重新处理maxrestart这么多次。如果处理maxrestart次之后还有待处理的softirq则会重新调度ksoftirqd继续处理。

tasklet implementation

  • void __init softirq_init(void);初始化softirq, 调用open_softirq(TASKLET_SOFTIRQ, tasklet_action); 将tasklet的回调函数tasklet_action注册到softirq的处理函数中。这样在有tasklet对应的softirq处理时就会调用tasklet_action

  • static void tasklet_action(struct softirq_action *a);这个函数主要有两个功能:

    • t->func(t->data);如果tasklet需要被调度,则运行它的回调函数
    • tasklet_trylock(t);测试tasklet是否在其他cpu上运行, test_and_set_bit(TASKLET_STATE_RUN, &(t)->state); 如果正在其他cpu上运行则重新发softirq请求延迟执行, __raise_softirq_irqoff(TASKLET_SOFTIRQ);

workqueue

kernel/workqueue.c

工作队列的具体实现比较复杂,但是它的原理很简单,就是创建内核线程来执行函数。

主要的实现函数

  • static void process_one_work(struct worker *worker, struct work_struct *work) 在worker线程上调用struct work_struct的回调函数。

主要的接口

请参考linux/workqueue.h

主要特点

  • 用内核线程实现,运行在进程上下文,回调函数可以睡眠
  • 在一个cpu上创建的work可能在其他cpu上运行
  • 不能访问进程的用户态地址,因为这些内核线程没有相应的用户线程
  • 可以指定延迟一段具体的时间执行

resources


Login *


loading captcha image...
(type the code from the image)
or Ctrl+Enter