Linux内核多线程小结(一)

一、内核线程介绍

Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。因为内核线程只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。

二、内核线程的基本函数

#define kthread_create(threadfn, data, namefmt, arg...) \
kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)

#define kthread_run(threadfn, data, namefmt, ...)  \

({   \
struct task_struct *__k  \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k))  \
wake_up_process(__k);  \
__k;   \
})

void kthread_bind(struct task_struct *k, unsigned int cpu);

int kthread_stop(struct task_struct *k);

int kthread_should_stop(void);

kthread_run()负责内核线程的创建,它由kthread_create()和wake_up_process()两部分组成,这样的好处是用kthread_run()创建的线程可以直接运行。外界调用kthread_run创建运行线程。kthread_run是个宏定义,首先调用kthread_create()创建线程,如果创建成功,再调用wake_up_process()唤醒新创建的线程。kthread_create()根据参数向kthread_create_list中发送一个请求,并唤醒kthreadd,之后会调用wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用schedule()调度出去。kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。

kthread_bind是将kthread_run创建的线程放到指定的CPU上执行。

kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志should_stop,并等待线程主动结束,返回线程的返回值。在调用 kthread_stop()结束线程之前一定要检查该线程是否还在运行(通过 kthread_run 返回的 task_stuct 是否有效),否则会造成灾难性的后果。kthread_run的返回值tsk。不能用tsk是否为NULL进行检查,而要用IS_ERR()宏定义检查,这是因为返回的是错误码,大致从0xfffff000~0xffffffff。

 kthread_should_stop()返回should_stop标志(参见 struct kthread )。它用于创建的线程检查结束标志,并决定是否退出。

三、内核线程的状态

linux下定义线程的状态:

#define TASK_RUNNING0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_STATE_MAX 512

设置线程状态的函数:

#define __set_task_state(tsk, state_value)\
do { (tsk)->state = (state_value); } while (0)
#define set_task_state(tsk, state_value) \
set_mb((tsk)->state, (state_value))
#define __set_current_state(state_value) \
do { current->state = (state_value); } while (0)
#define set_current_state(state_value) \
set_mb(current->state, (state_value))


四、常用的内核线程调度函数

int wake_up_process(struct task_struct *p);

int wake_up_state(struct task_struct *p, unsigned int state);

asmlinkage void __sched schedule(void);

 前面2个是wakeup函数是将休眠的线程唤醒的,后面schedule可以将线程休眠。我们常用的msleep, usleep等函数也会让当前线程进入休眠。

signal_pending

signal_pending( current )―――》检查当前进程是否有信号处理,返回不为0表示有信号需要处理。
-ERESTARTSYS表示信号函数处理完毕后重新执行信号函数前的某个系统调用。
也就是说,如果信号函数前有发生系统调用,在调度用户信号函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。

五、内核线程的同步机制

比较常用的有:

1)completion

wait_for_completion//等待completion的信号,在信号来之前,线程处于休眠状态

wait_for_completion_timeout

completion_done//发completion信号

2)信号量semaphore

sema_init

void down(struct semaphore *sem)

int down_interruptible(struct semaphore *sem)

void up(struct semaphore *sem)

3)event

wait_event_interruptible(wq, condition)//等待condition为ture,在这之前线程处于休眠

wait_event_interruptible_timeout(wq, condition, timeout)

wake_up(x)



后续拿一些linux代码的例子进行分析。


六、参考文献

http://www.cnblogs.com/zhuyp1015/archive/2012/06/13/2548494.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值