Linux内核线程kthread简介【最好的一篇!】

本文深入探讨Linux内核线程kthread的创建、退出过程,以及源码分析。通过kthread_create、kthread_stop函数了解内核线程的生命周期,并分析内核线程与用户线程的区别。文章包含kthreadd、keventd_create_kthread等关键函数的解析,适合Linux内核学习者参考。
摘要由CSDN通过智能技术生成

【推荐阅读】

深入linux内核架构--内存管理

关于如何快速学好,学懂Linux内核。内含学习路线

Linux内核CPU调度域内容讲解

需要多久才能看完linux内核源码?

带你走进linux 内核 定时器(timer)实现机制

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

内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。

内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。让模块在加载后能一直运行下去的方法——内核线程。要创建一个内核线程有许多种方法,我们这里要学的是最简单的一种。打开include/linux/kthread.h,你就看到了它全部的API,一共三个函数:

struct task_struct kthread_run(int (*threadfn)(void *data),  
    void *data, const char namefmt[],...);  
int kthread_stop(struct task_struct *k);  
int kthread_should_stop(void);

一、线程的创建

/**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
*/
#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;           \
})

这个函数的英文注释里很明确的说明: 创建并启动一个内核线程。可见这里的函数kthread_create()只是创建了内核线程,而后面的这个函数wake_up_process()则是启动了这个线程,让它在一开始就一直运行下去。直到遇见kthread_should_stop函数或者kthread_stop()函数。

学习地址(腾讯课堂):Linux内核源码/进程管理/内存管理/网络协议/设备驱动/文件系统

kthread_run实际是一个宏定义,它由kthread_create()和wake_up_process()两部分组成,调用了kthread_create后执行了wake_up_process.这样的好处是用kthread_run()创建的线程可以直接运行,使用方便。
kthread_run()负责内核线程的创建,参数包括入口函数threadfn,参数data,线程名称namefmt。可以看到线程的名字可以是类似sprintf方式组成的字符串。如果线程创建成功,再调用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参数中的入口函数。

struct kthread {
 
       int should_stop;
 
       struct completion exited;
 
};

kthread() (注:原型为:static int kthread(void *_create) )的实现在kernel/kthread.c中,头文件是include/linux/kthread.h。内核中一直运行一个线程kthreadd,它运行kthread.c中的kthreadd函数。在kthreadd()中,不断检查一个kthread_create_list链表。kthread_create_list中的每个节点都是一个创建内核线程的请求,kthreadd()发现链表不为空,就将其第一个节点退出链表,并调用create_kthread()创建相应的线程。create_kthread()则进一步调用更深层的kernel_thread()创建线程,入口函数设在kthread()中。

int kthreadd(void *unused)
{
 
       struct task_struct *tsk = current;
 
 
       /* Setup a clean context for our children to inherit. */
 
       set_task_comm(tsk, "kthreadd");
 
       ignore_signals(tsk);
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值