内核线程 中断上下文 睡眠

当我们谈到中断的时候,往往会提到异常。和中断不同的是,异常是依赖于processor时钟同步发生的,实际上异常经常被称作同步中断。异常是由processor发起的,当processor执行了某些编程错误或者是异常的情况的时候,这些异常情况需要被内核获知。

 

Interrupt Handlers

ISR: Interrupt Service Routine

ISR是个普通的C函数,运行在中断上下文

因为中断在任何时间都可能发生,因此,中断处理程序需要尽可能快的运行,这样可以尽可能快的恢复被打断的其他内核程序。

 

Top Halves Versus Bottom Halves

由于ISR需要尽可能快的运行,而同时一个中断又有可能需要做大量的工作,怎么办?Linux内核将对中断的处理分为两个部分:Top Half和Bottom Half,ISR是Top Half,而Bottom Half则是在之后的一个更合适的运行点运行。

 

Registering an Interrupt Handler


int request_irq(unsigned int irq,
                irqreturn_t (*handler)(int, void *, struct pt_regs *),
                unsigned long irqflags,
                const char *devname,
                void *dev_id)

void free_irq(unsigned int irq, void *dev_id)

 

Writing an Interrupt Handler

static irqreturn_t intr_handler(int irq, void *dev_id, struct pt_regs *regs) 

需要注意的是

ISR在Linux是不可重入的,当一个中断处理程序运行的时候,相应的中断线将被置Mark Out,无论是多cpu还是multi processor。

 

Interrupt Context

    当执行ISR或Bottom Half的时候,内核在终端上下文。中断上下文中,不可以sleep,因为没有对应的process,内核是不能进行重新调度的。忙等待循环是在终端上下文中禁止的。

    中断处理程序的stack,历史上,ISR没有自己独立的stack的,是同被ISR打断的process共享使用同一个stack。在早期的2.6内核中,可以选择减少stack从2个页到1个页,4KB。这样的话,ISR可以有自己的stack,每个processor可以拥有一个页大小的stack供这个processor上运行的ISR使用。

 

引自 http://blog.sina.com.cn/s/blog_5773e4e10100ktsk.html

 

 

 

1)中断处理程序(top half)中不能睡眠,是因为哪个原因:
a)没有进程上下文,睡眠之后不能重新调度?
b)中断程序可能关闭了所有中断(使用SA_INTERRUPT),那么再睡眠,就没有抢占点了(中断都进不来,哪里可以schedule()?)

2)kernel thread (ps 中名称类似"[xxx]"),可以睡眠,是因为kernel thread是有上下文,可以调度的,
那么kernel module作为kernel的一部分是否能够睡眠?为什么?如果能,它有上下文么?

3)除了内核线程,内核什么地方可以用semaphores? 即可以睡眠?

呵呵,有点混了,哪位朋友帮我分析一下,非常感谢!

楼主,可能有点把kernel处理和kernel thread处理以及中断处理弄混了。
记住:当程序跑在内核态的时候,只可能处于两种环境:中断上下文或者进程上下文
前者是中断驱动的,后者是用户进程/内核线程驱动的
当内核处于中断驱动部分时,没有上下文,是不能阻塞,睡眠的。这种情况适用于中断处理程序,以及你提到的packet handler,这是网站中断驱动的
当内核处于进程上下文时,是可以阻塞的,包括内核线程,也有自己的上下文。
关于你的问题:
1) a)是主要原因。
2)kernel中是看哪一部分,
有中断上下文的是不允许阻塞的。系统调用部分是可以的,内核线程也是可以的。

*)关于内核模块:以一个驱动程序举例,驱动程序中某部分是中断处理程序,那么处于中断上下文中,就不能睡眠。
如果通过系统调用从用户态到内核态(比如你的设备是字符设备,驱动程序中挂了open/read函数),此时就处于进程上下文,就可以睡眠。

*)关于内核线程:内核线程与普通的进程的区别是,内核线程没有独立的地址空间,它的mm指针被设置为NULL(p->mm = NULL),能访问内核地址空间,一般用于内核后台执行一些操作,从不切换到用户空间去。内核线程只能由其他内核线程创建。可以被调度,也可以被抢占。因为它有自己的task_struct,current宏就指向自己的task_struct,x86里在内核栈的尾端创建thread_info结构,通过计算偏移间接的查找task_struct结构。内核是在创建线程时分配内核栈的。

From  http://www.linuxquestions.org/li ... Linux_Kernel_Thread

The implementation of kernel_thread() function is available in arch/i386/kernel/process.c. The new task is created via do_fork() system call. A kernel_thread() system call is invoked with a special flag CLONE_KERNEL as an argument. This is defined in include/linux/sched.h as below in

define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)

Internally, kernel_thread() passes another two flags CLONE_VM and CLONE_UNTRACED to
do_fork() system call. It means a newly created process will share following resources with its parent process.

File System Information.
File Descriptor Table.
Signal Handlers.
Memory space.
A do_fork() is responsible to create a new task structure on the basis of the
flags passed to it, to set a state of newly created task to TASK_RUNNING and put
this task structure on a runqueue list. It depends upon scheduler when this task
is being picked up for execution.


*)关于系统调用:内核在执行系统调用的时候是处于进程上下文中,current指针指向当前任务,即引发系统调用的那个进程。所以,系统调用是可以睡眠的。

若有问题,欢迎讨论。
1) 是因为a), 没有进程上下文是最关键因素。

kernel module, 从module_init指定的函数起, 是运行于insmod这个进程的进程上下文的。 当然,如果你注册了中断、softirq等, 你写的那些处理函数就是运行在中断上下文。

除了irq和softirq,哪里都可以用semaphore

 

1)ISR中不能睡眠,没别的原因,就是因为中断什么时候来是不可预测的。
本来当前进程运行的好好的,突然来了个中断把当前进程睡眠掉了,而这个中断和当前进程什么关系都没有,有点象走大街上突然被天上掉石头砸一下的感觉。

被中断打断的进程或者线程技术上属于运行态,不是睡眠态。

在Solaris里是TS_ONPROC,在linux相信类似


2)kernel module是什么?是module吗?module无所谓睡眠不睡眠,因为一个module并不是一个进程或者线程。
有的module中,启动了一个内核线程,如usb总线驱动模块中启动了usb_hub_thread线程,那么这个线程倒是可以睡眠,唤醒.......


大部分内核模块和驱动程序都支持多线程,支持锁来同步互斥访问,这些都是可以引起睡眠的,当然睡眠的实体还是所服务的用户线程对应的内核线程。

中断等特殊的上下文在不同的系统上实现有差异,所有的系统上都存在不允许睡眠的特殊上下文,这时,是不允许睡眠的。

3)内核什么地方都可以用semaphores,只要down和up之间不睡眠。

锁的使用取决于上下文的约束条件,在不同系统上是有差异的。特殊的内核上下文里不允许睡眠。
转自 : http://blog.sina.com.cn/s/blog_62467c4b0100g557.html

 

你可能感兴趣的:(内核线程 中断上下文 睡眠)