linux内核线程是啥,用来干嘛的我就不一一赘述了,网上一搜一大把,此文只讲使用方法用于实际开发,不过如果是初学者的话大概的原理流程啥的最好可以去了解一下方便理解。
此教程基于linux内核4.19,接下来直接上干货!
一、线程创建
线程创建有两种方式:
1、创建好线程后再任意唤醒线程
#define kthread_create(int (*threadfn)(void *data),void *data,const char *namefmt)
下图是内核源码的描述(初学者最好能培养看内核源码的习惯,毕竟网上的资料不是万能的很多时候还是得自己看源码):
三个参数:
threadfn: 要执行的线程函数
data: 有需要给线程函数传递参数就往这传,不需要就NULL
namefmt: 线程名
返回值:
struct task_struct * 一个结构体指针,这个结构体原型很长很臭,只是简单使用的话不需要深究,想要了解可以自行去内核查看,我们只需要把返回值当做一个标志位,非NULL就是创建成功,NULL就是创建失败,并且需要此标志位来启动线程,所以我们创建线程前需要提前定义一个struct task_struct *的结构体来接收这个返回值。
创建好后就需要唤醒线程:
int wake_up_process(struct task_struct *p)
参数:kthread_create的返回值:struct task_struct *p
返回值:返回1 表示唤醒成功, 返回0表示线程正在运行。
2、创建好后直接运行线程
kthread_run(int (*threadfn)(void *data),void *data,const char *namefmt)
函数原型:
参数如上一致,并且可以看到实际的实现与第一种的方法是一样的。
3、线程销毁
如果不需要线程继续跑就销毁线程,一般都是用做异常处理或者卸载驱动需要,函数如下:
int kthread_stop(struct task_struct *k);
二、示例
两种方法根据自己实际需求选择即可,如果想要线程创建后晚一点再跑起来就用第一种(比如需要等驱动注册成功后再跑,也就是创建好线程等驱动加载成功后再唤醒线程),如果想要线程创建好就跑起来就用第二种。
这里以第一种方法简单写个示例:
#include <linux/kthread.h>
#include <linux/err.h>
struct task_struct *test_task = NULL; //建议进行初始化,防止出错
int test_thread(void *data)
{
while(1){ //根据自己实际情况做修改,实际很少这样一直暂用cpu的,会增加功耗,我们需要知道内核线程是可以阻塞的
printk("test for kthread\n");
if (kthread_should_stop()) //检查结束标志,并决定是否退出,由kthread_stop决定
break;
}
return 0;
}
static int32_t __init test_init(void)
{
test_task = kthread_create(test_thread,NULL,"test_thread"); //函数名,需要传递的数据不传递则NULL,创建的线程名
/*驱动的注册*/
/*.........*/
//唤醒线程
if(!IS_ERR(test_task)){
wake_up_process(test_task); //有需要的话可以监控一下返回值
}
}
static void __exit test_exit(void)
{
kthread_stop(test_task);
}
module_init(test_init);
module_exit(test_exit);
MODULE_DESCRIPTION("TEST Driver");
MODULE_LICENSE("GPL v2");
以上是简单示例,实际开发肯定没这么简单,经常会与中断上半部与下半部结合并且还需要考虑linux并发与竞争问题,后续有时间的话会继续出教程,感谢支持!