2021-04-10 Linux内核线程 kernel_thread、kthread_create、 kthread_run kthread_should_stop()
linux内核线程 kernel_thread(),kthread_create() kthread_run()一、内核经常需要在后台执行一些操作,这种任务就可以通过内核线程完成,内核线程是独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。实际
一、内核经常需要在后台执行一些操作,这种任务就可以通过内核线程完成,内核线程是独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。实际上,内核线程只能由其他内核线程创建,linux驱动模块中可以用kernel_thread(),kthread_create()/kthread_run()两种方式创建内核线程。
二、创建线程方式一:kernel_thread
2.1 kernel_thread()声明在include/linux/sched.h
2.2 参数说明:
fn:线程函数地址
arg:线程函数的形参,没有,可以是NULL
flags:标志,一般用CLONE_KERNEL(定义在linux/sched.h中,注意有的版本中,没用定义),其他标志及含义见uapi/linux/sched.h中。
返回值:返回线程ID值。
3.3 实际应用kernel\drivers\net\wireless\rockchip_wlan\cywdhd\bcmdhd\wl_iw.c
三、创建线程方式二:kthread_create
3.1 kernel\include\linux\kthread.h
3.2 函数代码
/**
* kthread_create_on_node - create a kthread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @node: task and thread structures for the thread are allocated on this node
* @namefmt: printf-style name for the thread.
*
* Description: This helper function creates and names a kernel
* thread. The thread will be stopped: use wake_up_process() to start
* it. See also kthread_run(). The new thread has SCHED_NORMAL policy and
* is affine to all CPUs.
*
* If thread is going to be bound on a particular cpu, give its node
* in @node, to get NUMA affinity for kthread stack, or else give NUMA_NO_NODE.
* When woken, the thread will run @threadfn() with @data as its
* argument. @threadfn() can either call do_exit() directly if it is a
* standalone thread for which no one will call kthread_stop(), or
* return when 'kthread_should_stop()' is true (which means
* kthread_stop() has been called). The return value should be zero
* or a negative error number; it will be passed to kthread_stop().
*
* Returns a task_struct or ERR_PTR(-ENOMEM) or ERR_PTR(-EINTR).
*/
struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
void *data, int node,
const char namefmt[],
...)
{
DECLARE_COMPLETION_ONSTACK(done);
struct task_struct *task;
struct kthread_create_info *create = kmalloc(sizeof(*create),
GFP_KERNEL);
if (!create)
return ERR_PTR(-ENOMEM);
create->threadfn = threadfn;
create->data = data;
create->node = node;
create->done = &done;
spin_lock(&kthread_create_lock);
list_add_tail(&create->list, &kthread_create_list);
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task);
/*
* Wait for completion in killable state, for I might be chosen by
* the OOM killer while kthreadd is trying to allocate memory for
* new kernel thread.
*/
if (unlikely(wait_for_completion_killable(&done))) {
/*
* If I was SIGKILLed before kthreadd (or new kernel thread)
* calls complete(), leave the cleanup of this structure to
* that thread.
*/
if (xchg(&create->done, NULL))
return ERR_PTR(-EINTR);
/*
* kthreadd (or new kernel thread) will call complete()
* shortly.
*/
wait_for_completion(&done);
}
task = create->result;
if (!IS_ERR(task)) {
static const struct sched_param param = { .sched_priority = 0 };
va_list args;
va_start(args, namefmt);
vsnprintf(task->comm, sizeof(task->comm), namefmt, args);
va_end(args);
/*
* root may have changed our (kthreadd's) priority or CPU mask.
* The kernel thread should not inherit these properties.
*/
sched_setscheduler_nocheck(task, SCHED_NORMAL, ¶m);
set_cpus_allowed_ptr(task, cpu_all_mask);
}
kfree(create);
return task;
}
3.3 kthread_create参数说明:
threadfn:线程函数地址
data:线程函数形参,如果没有可以定义为NULL
namefmt,arg…:线程函数名字,可以格式化输出名字。
返回值:返回线程指针(strcut task_struct *)
3.4 kthread_create创建线程流程
rest_init:
调用
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //2号进程
创建了进程kthreadd
kthreadd:
在for死循环中,判断kthread_create_list链表是否为空;如果为空,调用schedule()切换进程,让出cpu。
当kthread_create里面调用wake_up_process(kthreadd_task)唤醒kthreadd时,程序则接着schedule,接着往下运行;然后在一个while循环中循环的遍历kthread_create_list,并取出节点,创建线程,创建完成然后删除节点,直到kthread_create_list链表为空跳出while循环,在for死循环重新运行
kthread_create:
1 : 构造了一个kthread_create_info 结构体,将其挂接到 kthread_create_list链表上
2 : 调用wake_up_process(kthreadd_task),唤醒内核线程kthreadd
kthread_create并没有创建线程,他只是将进程结构体kthread_create_info添加到kthread_create_list,然后唤醒2号进程,线程的创建工作是在kthreadd中调用do_fork去创建的。
3.5 实例如下:
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>
MODULE_AUTHOR("zimu");
MODULE_LICENSE("GPL");
#define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
strcut task_struct *practice_task_p = NULL;
int my_kernel_thread(void *arg)
{
int n = 0;
while(1)
{
printk("%s: %d\n",__func__,n++);
ssleep(3);
if(kthread_should_stop())
{
break;
}
}
return 0;
}
static int __init practiceCall(void)
{
printk("%s:\n",__func__);
practice_task_p = kthread_create(my_kernel_thread,NULL,"practice task");
if(!IS_ERR(practice_task_p))
wake_up_process(practice_task_p);
return 0;
}
static void __exit practiceCallExit(void)
{
printk("%s:\n",__func__);
kthread_stop(practice_task_p);
}
module_init(practiceCall);
module_exit(practiceCallExit);
3.6 kthread_create()创建后,线程没有立即运行,需要将返回的值,即线程指针(struct task_struct *),作为参数传入到wake_up_process()唤起线程运行.
唤醒内核线程(可以唤醒所有进程(线程)):
wake_up_process(struct task_struct *k);
wake_up_process函数解析
函数schedule()实现调度程序。它的任务是从运行队列的链表rq中找到一个进程,并随后将CPU分配给这个进程。
四、创建线程方式三:kthread_run
4.1 kernel\include\linux\kthread.h
/**
* 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; \
})
4.2 参数说明:
threadfn:线程函数地址
data:线程函数形参,没有可以制定为NULL
namefmt, …:线程名字,可以格式化输出
返回值__k:线程结构体指针(struct task_struct * )
4.3 kthread_run()创建的线程,理解已经有wake_up_process,立刻运行。
4.4 实例
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>
MODULE_AUTHOR("tanghanyue");
MODULE_LICENSE("GPL");
#define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
int thy_kernel_thread(void *arg)
{
int n = 0;
while(1)
{
printk("%s: %d\n",__func__,n++);
ssleep(3);
if(kthread_should_stop())
{
break;
}
}
return 0;
}
static int __init practiceCall(void)
{
printk("%s:\n",__func__);
practice_task_p = kthread_run(thy_kernel_thread,NULL,"practice task");
return 0;
}
static void __exit practiceCallExit(void)
{
printk("%s:\n",__func__);
kthread_stop(practice_task_p);
}
module_init(practiceCall);
module_exit(practiceCallExit);
4.5退出线程kthread_stop:通过发送信号给线程,使之退出。
int kthread_stop(struct task_struct *thread);线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。 但如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止,因此,线程函数必须能让出CPU,以便能运行其他线程。同时线程函数也必须能重新被调度运行。
五、一般的实际应用1,在串口调试信息的使用。
static int console_thread(void *data)
{
struct platform_device *pdev = data;
char *buf = console_buf;
unsigned int len;
while (1) {
unsigned int dropped;
set_current_state(TASK_INTERRUPTIBLE);
if (kfifo_is_empty(&fifo))
schedule();
if (kthread_should_stop())
break;
set_current_state(TASK_RUNNING);
while (!console_thread_stop) {
len = kfifo_out(&fifo, buf, LINE_MAX);
if (!len)
break;
console_put(pdev, buf, len);
}
dropped = console_dropped_messages;
if (dropped && !console_thread_stop) {
console_dropped_messages = 0;
smp_wmb();
len = snprintf(buf, LINE_MAX,
"** %u console messages dropped **\n",
dropped);
console_put(pdev, buf, len);
}
if (!console_thread_stop)
console_flush(pdev);
}
return 0;
}
#ifdef CONFIG_RK_CONSOLE_THREAD
t->console_task = kthread_run(console_thread, pdev, "kconsole");
if (!IS_ERR(t->console_task))
t->pdata.console_write = console_write;
#endif
六、实例二,模拟pwm
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
//API for libgpio
#include <linux/gpio.h>
//API for malloc
#include <linux/slab.h>
//API for device tree
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
//API for thread
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/pwm.h>
#include <drm/drmP.h>
static struct task_struct *thread_body;
struct gpio_demo_priv{
int count;
int gpio[0];
struct mutex mtx;
int mode;
};
struct gpio_demo_priv *priv;
static long rk3288_giada_gpio_pwm=255;
static ssize_t mipibrightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
struct pwm_device * pwm1 = NULL;
pwm1 = pwm_request(1, "backlight-pwm");
if (IS_ERR(pwm1)) {
ret = PTR_ERR(pwm1);
if (ret != -EPROBE_DEFER)
printk( "unable to request PWM1\n");
printk( "Unable to request PWM1\n");
return sprintf(buf, "unable to request PWM1\n");
}
pwm_config(pwm1, 500000, 1000000);
pwm_enable(pwm1);
return sprintf(buf, "%ld\n", rk3288_giada_gpio_pwm);
}
static ssize_t mipibrightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
struct pwm_device * pwm2 = NULL;
pwm2 = pwm_request(2, "mipibacklight-pwm");
pwm_config(pwm2, 500000, 1000000);
pwm_enable(pwm2);
//unsigned long brightness;
rc = kstrtoul(buf, 0, &rk3288_giada_gpio_pwm);
return rc ? rc : count;
}
static DEVICE_ATTR(mipibrightness, S_IWUSR | S_IRUSR, mipibrightness_show, mipibrightness_store);
static struct attribute *bl_device_attrs[] = {
&dev_attr_mipibrightness.attr,
NULL,
};
static const struct attribute_group bl_attr_group = {
.attrs = bl_device_attrs,
};
static int thread_func(void *data) {
//int count;
while (1){
//count++;
/*
mutex_lock(&priv->mtx);
for ( i = 0; i < priv->count; i++){
//gpio_set_value(priv->gpio[i], count%2);
}
mutex_unlock(&priv->mtx);
*/
//printk(KERN_INFO "thread count %d priv->count=%d priv->gpio[i]=%d\n", count,priv->count,priv->gpio[0]);
//if(count%rk3288_giada_gpio_pwm==0)
gpio_set_value(priv->gpio[0], 1);
ndelay(20000*rk3288_giada_gpio_pwm/255);
if(rk3288_giada_gpio_pwm==255)
continue;
gpio_set_value(priv->gpio[0], 0);
ndelay(20000*(255-rk3288_giada_gpio_pwm)/255);
}
return 0;
}
static int gpio_demo_probe(struct platform_device *pdev){
int ret,i;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
if (!node)
return -EINVAL;
ret = of_gpio_count(node);
if (ret == 0){
return -EINVAL;
}
priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(int) * ret, GFP_KERNEL);
if (!priv){
return -ENOMEM;
}
priv->count = ret;
mutex_init(&priv->mtx);
for (i = 0; i < priv->count; i++) {
unsigned int gpio;
gpio = of_get_gpio(node, i);
if (gpio < 0) {
dev_warn(dev, "Unable to get gpio #%d\n", i);
continue;
}
ret = devm_gpio_request_one(dev, gpio, GPIOF_DIR_OUT, pdev->name);
priv->gpio[i] = gpio;
if (ret < 0) {
dev_warn(dev, "Unable to re quest GPIO %d: %d\n",
gpio, ret);
continue;
}
printk(KERN_INFO "success request gpio=%d priv->gpio[i]=%d\n",gpio,priv->gpio[i]);
gpio_direction_output(gpio, 1); //设置输出的电平
}
ret = sysfs_create_group(&dev->kobj, &bl_attr_group);
if (ret < 0)
dev_warn(dev, "attr group create failed\n");
else
dev_info(dev, "attr group create success!\n");
platform_set_drvdata(pdev,priv);
printk("gpio_demo_probe kthread_create\r\n ");
thread_body = kthread_create(thread_func, NULL, "thread_pwm");
if((thread_body))
{
wake_up_process(thread_body);
}
return 0;
}
static struct of_device_id gpio_demo_of_match[] = {
{ .compatible = "giada-gpio-pwm"},
{},
}
MODULE_DEVICE_TABLE(of,gpio_demo_of_match);
static struct platform_driver gpio_demo_driver = {
.probe = gpio_demo_probe,
.driver = {
.name = "gpio-demo-device",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_demo_of_match),
}
};
static int __init gpio_demo_init(void){
return platform_driver_register(&gpio_demo_driver);
}
static void __exit gpio_demo_exit(void){
platform_driver_unregister(&gpio_demo_driver);
}
late_initcall(gpio_demo_init);
module_exit(gpio_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Gpio demo Driver");
MODULE_ALIAS("platform:gpio-demo");
七、用ps 或者top命令查看是否已经创建线程
八、有价值的参考文章
https://www.codenong.com/17795739/
运行不息的内核线程kthread_kthread_stop卡死_angle_birds的博客-CSDN博客
更多推荐
所有评论(0)