【内核】内核线程

一、概述

内核线程是直接由内核本身启动的进程。内核线程实际上是将内核函数委托给独立的进程执行,它与内核中的其他“进程”并行执行。内核线程经常被称之为内核守护进程。

  • 地址空间:内核线程没有自己的地址空间、只能和其它线程共享主线程的地址空间和页表、并且只可以访问虚拟地址空间的内核部分
  • 线程创建:linux所有任务的祖先是0号进程,然后0号进程创建了天字第一号init进程,kthreadd内核线程的pid是2,它创建了其他所有线程,包括内核线程。
  • 线程调度优先级:Linux会把进程分为普通进程和实时进程,普通进程采用CFS之类调度算法,而实时进程则是采用SCHED_FIFO或SCHED_RR。 内核使用如下三个接口来修改内核线程优先级 sched_set_fifo§ sched_set_fifo_low§ sched_set_normal(p, nice)
  • 线程状态:内核线程与其他进程或者线程同样的线程状态:
    • R (TASK_RUNNING)
    • S (TASK_INTERRUPTIBLE)
    • D (TASK_UNINTERRUPTIBLE)
    • T (TASK_STOPPED or TASK_TRACED)
    • Z (TASK_DEAD - EXIT_ZOMBIE)
    • X (TASK_DEAD - EXIT_DEAD)
  • 线程描述符:内核线程和用户进程一样使用task_struct结构体描述线程信息,task_struct结构一直在增大。如果将此结构放在内核栈中则很浪费内核栈的空间,则在threadinfo结构中用一个task_struct的指针就可以避免,而threadinfo结构与内核栈是一个union,获取当前堆栈指针SP,就能得到threadinfo地址,于是也能获取到当前线程描述符task_struct。
struct thread_info {
    unsigned long        flags;        /* low level flags */
    mm_segment_t        addr_limit;    /* address limit */
    struct task_struct    *task;        /* main task structure */
    int            preempt_count;    /* 0 => preemptable, <0 => bug */
    int            cpu;        /* cpu */
}
union thread_union {
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
}
#define THREAD_SIZE        16384
#define THREAD_START_SP        (THREAD_SIZE - 16)

static inline struct thread_info *current_thread_info(void) {
    return (struct thread_info *)
        (current_stack_pointer & ~(THREAD_SIZE - 1));
}
#define get_current() (current_thread_info()->task)
#define current get_current()
register unsigned long current_stack_pointer asm ("sp");

在这里插入图片描述
新版本内核中开启CONFIG_THREAD_INFO_IN_TASK宏,这时候thread_info就会在task_struct的第一个成员。而task_struct中依然存在void* stack结构,thread_info也不存在与内核堆栈中,那现在如何获取线程描述符,arm64硬件架构用了sp_el0寄存器用于单独存放当前task_struct地址。

static __always_inline struct task_struct *get_current(void) {
    unsigned long sp_el0; 
    asm ("mrs %0, sp_el0" : "=r" (sp_el0));
    return (struct task_struct *)sp_el0;
}
#define current get_current()

在这里插入图片描述

二、函数接口

线程启动起来后,可以主动调用do_exit函数退出,或者其他的进程调用kthread_stop函数,结束线程的运行。线程是否可以直接return,待确认,可能存在相关资源没有回收。
使用kthread_stop函数时,要求线程一定没有退出,而且能够响应并退出。
module_exit 时,需要保证相关线程已经退出,否则会报错。

int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); // fn为线程函数,arg为线程函数参数,flags为标记
void daemonize(const char * name,...); // name为内核线程的名称
kthread_create(threadfn, data, namefmt, arg...)
int wake_up_process(struct task_struct *p)
kthread_run(threadfn, data, namefmt, ...) // kthread_create和wake_up_process的集合
int kthread_stop(struct task_struct *k)
void kthread_bind(struct task_struct *p, unsigned int cpu)
bool kthread_should_stop(void)

三、代码示例

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#define hp_prt(fmt, arg...)  printk("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)

static struct task_struct *ptask2;
static struct task_struct *ptask1;
static int g_exit;
int phtread_fun_1(void *data)
{
    hp_prt(" enter!\n");

    while(1){
        hp_prt("do while.\n");
        if(kthread_should_stop()) {
            ptask1 = NULL;
            hp_prt("phtread_fun_1 out!\n");
            return 0;
        }
        msleep(10000);
    }

    return 0;
}
int phtread_fun_2(void *data)
{
    hp_prt("enter!\n");

    while(1){
        hp_prt("do while.\n");
        if(g_exit) {
            printk("phtread_fun_2 out before!\n");
            do_exit(0);
            printk("phtread_fun_2 out!\n");
            return 0;
        }
        msleep(1000);
    }
    return 0;
}

static int code_case_thread_init(void)
{
    hp_prt("enter!\n");
 
    ptask1 = kthread_create(phtread_fun_1, NULL, "task1");
    if(IS_ERR(ptask1)){
        hp_prt("create task1 error!\n");
    } else {
        wake_up_process(ptask1);
    }
    ptask2 = kthread_run(phtread_fun_2, NULL, "task2");
    if (ptask2 == NULL) {
        hp_prt("create task2 error!\n");
    }
 
    return 0;
}

static void code_case_thread_exit(void)
{
    hp_prt("enter\n");

    if (ptask1 != NULL)
        kthread_stop(ptask1);
    hp_prt(" task2 exit!\n");
    g_exit = 1;
    msleep(10000); // 要保证task2 已经退出
    hp_prt("out!\n");
}
MODULE_AUTHOR("hpz");
MODULE_LICENSE("Dual BSD/GPL");
module_init(code_case_thread_init);
module_exit(code_case_thread_exit);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值