linux内核线程创建销毁机制
这个话题乍一听貌似比较大,其实线程创建本身就是一件很平常的事情。
下面将要介绍的是,新版linux中创建内核线程的机制做了一些变化(其实本质没变,最终还是调用do_fork()来实现),和控制线程的时候需要注意的地方。
本文引用的几个源码文件:
@ kernel/kernel/kthread.c
@ kernel/include/linux/kthread.h
@ kernel/include/linux/wait.h
@ kernel/kernel/workqueue.c
新版linux中将创建内核线程的工作交给了一个专门的内核线程kthreadd去做了,该线程会检查全局链表kthread_create_list,如果为NULL,就会调schedule()放弃cpu进入睡眠状态,否则就取下该链表中的一项出来创建对应的线程。
kthreadd线程何时创建?
kthreadd线程在系统启动阶段被创建,创建代码如下:
start_kernel() @ kernel/init/main.c
--> rest_init()
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
struct task_struct *kthreadd_task; @ kernel/kernel/kthread.c
-- 记录这线程kthreadd的task_struct结构体
该线程的线程函数是kthreadd() @ kernel/kernel/kthread.c -- 见代码
我们平时使用的创建接口?
@ kernel/include/linux/kthread.h
#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()函数来创建,值得注意的是,该函数创建线程ok返回时,新建线程是休眠的。代码里可以看到休眠的位置。所以如果需要创建线程后并马上运行,kthread_run()是个不错的接口。
kthread_create()函数?
先看两个相关的数据结构:
struct kthread_create_info
{
int (*threadfn)(void *data);
void *data;
struct task_struct *result;
struct completion done;
struct list_head list;
};
struct kthread {
int should_stop;
struct completion exited;
};
该函数的实现见代码:kernel/kernel/kthread.c
create_kthread()函数时线程kthreadd被唤醒之后调用的函数,该函数调用函数:
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
其中kthread()是创建的内核线程的共用线程函数,我们在上层接口中传递下来的线程函数和参数都包含在参数create中。在函数kthread()才会调用我们传递进来的函数。
详见代码:kernel/kernel/kthread.c
如何停止某个线程?
使用函数kthread_stop(struct task_struct *k)即可停止一个指定的线程。但是有时候停止某线程的时候需要满足一定条件才可以成功让线程停止并销毁资源,这个稍后会提到。这里先看该函数源码: kernel/kernel/kthread.c
注意事项
这个问题是在停止线程的时候需要注意的。
如果你的线程没事做的时候是睡眠的,而起睡眠在某个等待队列头上等某个条件满足,那么在停止该线程之前必须让那个条件满足,才能调用kthread_stop()函数,否则的话,你永远也停止不掉这个线程,除非kill -9结果了它。
因为可睡眠的线程通常是调用wait_event_interruptible(wq, condition)宏来等待条件,通常的线程被设置成可被信号中断地等。
#define __wait_event_interruptible(wq, condition, ret) /
do { /
DEFINE_WAIT(__wait); /
/
for (;;) { /
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); /
if (condition) /
break; /
if (!signal_pending(current)) { /
schedule(); /
continue; /
} /
ret = -ERESTARTSYS; /
break; /
} /
finish_wait(&wq, &__wait); /
} while (0)
看的出来,线程被唤醒后首先再次去检查条件是否满足,条件满足才会退出睡眠,否则检查一下如果没有信号,会继续睡下去。所以kthread_stop()中只有一次唤醒这个线程的机会,错过了就错过了,除非另外的地方满足了这个条件。所以在停止线程之前,满足一下它等待的条件是非常可靠的。
另外一个情况如下,如果你的线程函数是这样写的:
struct sched_param param = { .sched_priority = RTPM_PRIO_TPD };
sched_setscheduler(current, SCHED_RR, ¶m);
do{
set_current_state(TASK_INTERRUPTIBLE);
wait_event_interruptible(waiter,条件); // 条件等待 tpd_flag!=0
清除条件 // tpd_flag = 0;
set_current_state(TASK_RUNNING);
if(kthread_should_stop())
continue;
{
数据读取处理
}
}while(!kthread_should_stop())
红色部位可能有时候时多余的,但是针对有些硬件的特点,在没有让你去读取数据的时候你读数据的时候往往会出错,因为这一次的唤醒根本就不是硬件说它已经准备好了,而是人为故意满足了这个条件而已。
如果你的线程写法是数据处理在前,检查睡眠在后,那么就没有这个添加的必要了。
int kthread_should_stop(void)
{
return to_kthread(current)->should_stop;
}
kthread_stop()函数中会做这样的操作:kthread->should_stop = 1;
最后的一种情况是,你的线程函数中没有等待某个条件,而是主动睡眠,然后其他地方根据你的线程的名字来唤醒的话,那么也就没有在停止之前人为满足其条件的动作了。
参考代码:kernel/kernel/workqueue.c worker_thread()函数,这个是工作者线程的线程函数。
linux内核线程创建销毁机制相关推荐
- 【Linux 内核】进程管理 ( 内核线程概念 | 内核线程、普通进程、用户线程 | 内核线程与普通进程区别 | 内核线程主要用途 | 内核线程创建函数 kernel_thread 源码 )
文章目录 一.内核线程概念 二.内核线程.普通进程.用户线程 三.内核线程.普通进程区别 四.内核线程主要用途 五.内核线程创建函数 kernel_thread 源码 一.内核线程概念 直接 由 Li ...
- linux kernel and user space通信机制,Linux内核与用户空间通信机制研究.pdf
ISSN 1009-3044 E-mail:info@CCCC.net.CR ComputerKnowledgeandTechnology电脑知识与技术 http://www.dnzs.net.cn ...
- 【Linux 内核】宏内核与微内核架构 ( 操作系统需要满足的要素 | 宏内核 | 微内核 | Linux 内核动态加载机制 )
文章目录 一.操作系统需要满足的要素 二.宏内核 三.微内核 四.Linux 内核动态加载机制 一.操作系统需要满足的要素 电脑上运行的 操作系统 , 是一个 软件 ; 设备管理 : 操作系统需要 为 ...
- Linux内核中的platform机制
Linux内核中的platform机制 从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver.Linux中大部分的设备驱动,都可以使用 ...
- linux 信号优先级,linux内核中的信号机制
linux内核中的信号机制--信号处理 Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123(http://blog.csdn.net ...
- linux 内核 工作队列,Linux内核新旧工作队列机制的剖析和比较
摘要:在中断驱动的程序设计中,工作队列是一种强有力的工具.但是在Linux2.6.35及其以前的内核版本中,每创建一个工作队列就创建与CPU数目相同的内核线程,耗费大量的内核资源:工作只能严格串行的处 ...
- Linux内核学习笔记 - RCU机制总结
目录 一.什么是RCU机制 1.历史背景 -- 原始的RCU思想 2.基础架构 -- RCU算法设计 3.实现思路-- 读写回收实现思路 4.实现思路 -- 实例说明 宽限期 订阅--发布机制 数据读 ...
- Linux内核线程kernel thread详解--Linux进程的管理与调度(十)【转】
转自:https://blog.csdn.net/gatieme/article/details/51589205 版权声明:本文为博主原创文章 && 转载请著名出处 @ http:/ ...
- linux内核线程socket,从Linux源码看Socket(TCP)的accept
从Linux源码看Socket(TCP)的accept 前言 笔者一直以为若是能知道从应用到框架再到操做系统的每一处代码,是一件Exciting的事情. 今天笔者就从Linux源码的角度看下Serve ...
最新文章
- python怎么使用int四舍五入_python中如何取整数
- 怎么在页面中使用mixins_模压化粪池使用过程中怎么管理?
- Spring-BeanFactory源码分析
- 正则表达式的基本语法
- 在使用ToolBar + AppBarLayout,实现上划隐藏Toolbar功能,遇到了一个坑。
- SPI 读取不同长度 寄存器_SPI协议,MCP2515裸机驱动详解
- visual studio开发工具的C#主流控件属性一览表
- htmlselect保留上次选择内容_搬家买什么工具,搬家有什么技巧,搬家选择什么公司?...
- WebRequest 对象的使用
- python实现将子文件夹下的图片复制到新的文件夹并重命名
- 那些年,我爬过的北科(十)——搜索案例之ElasticSearch的使用
- 顺序结构—— 华氏温度转摄氏温度
- java 导出Excel表头
- PC端自适应使用rem
- MPEG4与.mp4
- 伪随机二进制序列(PRBS)
- 获取最新、最全的小红书地理位置签到数据。
- API解读:StringTokenizer
- 如何通过OKR工具帮助日常工作落地
- 爬取网易云音乐某一个人的评论
热门文章
- 外企员工职场跃迁:他们将目光转向杭州 转向云计算
- 高性能mysql:创建高性能的索引
- 大数据流通与交易技术国家工程实验室成立大会在京举行
- 思科宣布NB-IoT平台实现商用
- Spring Boot开始
- 【中国超算迎来最强对手】 IBM推出机器学习加速“瑞士军刀”Power9芯片,性能为同类产品的10倍...
- Linux常见面试题2
- CSS 定位之绝对与相对
- SVN的搭建及使用(三)用TortoiseSVN修改文件,添加文件,删除文件,以及如何解决冲突,重新设置用户名和密码等...
- 谁说Vim不是IDE?(四)