linux有内核级线程么

linux有内核级线程,linux支持内核级的多线程。Linux内核可以看作服务进程(管理软硬件资源,响应用户进程的各种进程);内核需要多个执行流并行,为了防止可能的阻塞,支持多线程。内核线程就是内核的一个分身,可以用以处理一件特定事情,内核线程的调度由内核负责,一个内核线程的处于阻塞状态时不影响其他的内核线程。

linux有内核级线程么

本教程操作环境:linux7.3系统、Dell G3电脑。

线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两种类型:“用户级线程”和“内核级线程”。

用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。

另外一种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部需求进行创建和撤销,这两种模型各有其好处和缺点。

用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占用了更多的系统开支。

Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程。

豆包AI编程 豆包AI编程

豆包推出的AI编程助手

豆包AI编程 483 查看详情 豆包AI编程

linux中的内核级线

1.内核线程概述

Linux内核可以看作服务进程(管理软硬件资源,响应用户进程的各种进程)

内核需要多个执行流并行,为了防止可能的阻塞,支持多线程。

内核线程就是内核的一个分身,可以用以处理一件特定事情,内核线程的调度由内核负责,一个内核线程的处于阻塞状态时不影响其他的内核线程。

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

周期性地将修改的内存页与页来源块设备同步实现文件系统的事务日志

内核线程由内核创建,所以内核线程在内核态执行,只能访问内核虚拟地址空间,不能访问用户空间。

在linux所有的线程都当作进程来实现,也没有单独为线程定义调度算法以及数据结构,一个进程相当于包含一个线程,就是自身,多线程,原本的线程称为主线程,他们一起构成线程组。

进程拥有自己的地址空间,所以每个进程都有自己的页表,而线程却没有,只能和其它线程共享主线程的地址空间和页表

2.三个数据结构

每个进程或线程由三个重要的数据结构,分别是struct thread_info, struct task_struct 和内核栈。

thread_info对象存放的进程/线程的基本信息,它和进程/线程的内核栈存放在内核空间里的一段2倍页长空间中。其中thread_info结构存放在地址段的末尾,其余空间作为内核栈。内核使用伙伴系统分配这段空间。

linux有内核级线程么1.png
struct thread_info {intpreempt_count;/* 0 => preemptable,  bug */struct task_struct*task;/* main task structure */__u32cpu;/* cpu */};

thread_info结构体中有一个struct task_struct *task,task指向该线程或者进程的task_struct对象,task_struct也叫做任务描述符:

struct task_struct {pid_t pid;pid_t tgid;void *stack;struct mm_struct *mm, *active_mm;/* filesystem information */struct fs_struct *fs;/* open file information */struct files_struct *files;};#define task_thread_info(task)((struct thread_info *)(task)->stack)

stack:是指向进程或者线程的thread_infomm:对象用来管理该进程/线程的页表以及虚拟内存区active_mm:主要用于内核线程访问主内核页全局目录pid:每个task_struct都会有一个不同的id,就是pidtgid:线程组领头线程的PID,就是主线程的pid

linux系统上虚拟地址空间分为两个部分:供用户态程序访问的虚拟地址空间和供内核访问的内核空间。每当内核执行上下文切换时,虚拟地址空间的用户层部分都会切换,以便匹配运行的进程,内核空间的部分是不会切换的。

3.内核线程创建

在内核版本linux-3.x以后,内核线程的创建被延后执行,并且交给名为kthreadd 2号线程执行创建过程,但是kthreadd本身是怎么创建的呢?过程如下:

pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags){return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,(unsigned long)arg, NULL, NULL);}pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

kthreadadd本身最终是通过do_fork实现的,do_fork通过传入不同的参数,可以分别用于创建用户态进程/线程,内核线程等。当kthreadadd被创建以后,内核线程的创建交给它实现。

内核线程的创建分为创建和启动两个部分,kthread_run作为统一的接口,可以同时实现,这两个功能:

#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;   })#define kthread_create(threadfn, data, namefmt, arg...) kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)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;/*分配kthread_create_info空间*/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;/*加入到kthread_creta_list列表中,等待ktherad_add中断线程去创建改线程*/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;...kfree(create);return task;}

kthread_create_on_node函数中:

首先利用kmalloc分配kthread_create_info变量create,利用函数参数初始化create将create加入kthread_create_list链表中,然后唤醒kthreadd内核线程创建当前线程唤醒kthreadd后,利用completion等待内核线程创建完成,completion完成后,释放create空间

下面来看下kthreadd的处理过程:

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);set_cpus_allowed_ptr(tsk, cpu_all_mask);set_mems_allowed(node_states[N_MEMORY]);current->flags |= PF_NOFREEZE;for (;;) {set_current_state(TASK_INTERRUPTIBLE);if (list_empty(&kthread_create_list))schedule();__set_current_state(TASK_RUNNING);spin_lock(&kthread_create_lock);while (!list_empty(&kthread_create_list)) {struct kthread_create_info *create;create = list_entry(kthread_create_list.next,    struct kthread_create_info, list);list_del_init(&create->list);spin_unlock(&kthread_create_lock);create_kthread(create);spin_lock(&kthread_create_lock);}spin_unlock(&kthread_create_lock);}return 0;}

kthreadd利用for(;;)一直驻留在内存中运行:主要过程如下:

检查kthread_create_list为空时,kthreadd让出cpu的执行权kthread_create_list不为空时,利用while循环遍历kthread_create_list链表每取下一个链表节点后调用create_kthread,创建内核线程

static void create_kthread(struct kthread_create_info *create){int pid;/* We want our own signal handler (we take no signals by default). */pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);if (pid done, NULL);if (!done) {kfree(create);return;}create->result = ERR_PTR(pid);complete(done);}}

可以看到内核线程的创建最终还是和kthreadd一样,调用kernel_thread实现。

static int kthread(void *_create){..../* If user was SIGKILLed, I release the structure. */done = xchg(&create->done, NULL);if (!done) {kfree(create);do_exit(-EINTR);}/* OK, tell user we're spawned, wait for stop or wakeup */__set_current_state(TASK_UNINTERRUPTIBLE);create->result = current;complete(done);schedule();ret = -EINTR;if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {__kthread_parkme(&self);ret = threadfn(data);}/* we can't just return, we must preserve "self" on stack */do_exit(ret);}

kthread以struct kthread_create_info 类型的create为参数,create中带有创建内核线程的回调函数,以及函数的参数。kthread中,完成completion信号量的处理,然后schedule让出cpu的执行权,等待下次返回 时,执行回调函数threadfn(data)。

4.内核线程的退出

线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。

int kthread_stop(struct task_struct *k){struct kthread *kthread;int ret;trace_sched_kthread_stop(k);get_task_struct(k);kthread = to_live_kthread(k);if (kthread) {set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);__kthread_unpark(k, kthread);wake_up_process(k);wait_for_completion(&kthread->exited);}ret = k->exit_code;put_task_struct(k);trace_sched_kthread_stop_ret(ret);return ret;}

如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。在执行kthread_stop的时候,目标线程必须没有退出,否则会Oops。所以在创建thread_func时,可以采用以下形式:

thread_func(){    // do your work here    // wait to exit    while(!thread_could_stop())    {           wait();    }}exit_code(){     kthread_stop(_task);   //发信号给task,通知其可以退出了}

如果线程中在等待某个条件满足才能继续运行,所以只有满足了条件以后,才能调用kthread_stop杀掉内核线程。

5.内核线程使用

#include "test_kthread.h"#include #include #include #include #include static struct task_struct *test_thread = NULL;unsigned int time_conut = 5;int  test_thread_fun(void *data){int times = 0;while(!kthread_should_stop()){printk("n   printk %urn", times);times++;msleep_interruptible(time_conut*1000);}printk("n   test_thread_fun exit successrnn");return 0;}void register_test_thread(void){test_thread = kthread_run(test_thread_fun , NULL, "test_kthread" );    if (IS_ERR(test_thread)){         printk(KERN_INFO "create test_thread failed!n");     }      else {         printk(KERN_INFO "create test_thread ok!n");      } }static ssize_t kthread_debug_start(struct device *dev, struct device_attribute *attr, char *buf){register_test_thread();return 0;}static ssize_t kthread_debug_stop(struct device *dev, struct device_attribute *attr, char *buf){kthread_stop(test_thread);return 0;}static DEVICE_ATTR(kthread_start,  S_IRUSR,  kthread_debug_start,NULL);static DEVICE_ATTR(kthread_stop, S_IRUSR,    kthread_debug_stop,NULL);struct attribute * kthread_group_info_attrs[] = {&dev_attr_kthread_start.attr,&dev_attr_kthread_stop.attr,NULL,};struct attribute_group kthread_group = {.name = "kthread",.attrs = kthread_group_info_attrs,};

相关推荐:《Linux视频教程》

以上就是linux有内核级线程么的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/253116.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Spring声明式事务的配置陷阱与正确使用方案
上一篇 2026年10月9日 09:18:55
c++中std::function和函数指针的区别_c++函数封装与可调用对象解析
下一篇 2025年12月19日 07:45:53

相关推荐

  • linux建立group是什么意思

    linux建立group是什么意思linux建立group是什么意思linux建立group是什么意思linux建立group是什么意思

    在linux,建立group的意思为“建立用户组”,可以使用groupadd命令实现。groupadd命令用于创建一个新的用户组,新用户组的信息将被添加到系统文件中,语法“groupadd [options] groupname”;参数“options”是groupadd命令使用的参数,而“grou…

    2026年10月9日 • 用户投稿
    100
  • linux重命名文件怎么实现

    linux重命名文件怎么实现linux重命名文件怎么实现linux重命名文件怎么实现linux重命名文件怎么实现

    linux重命名文件的方法:1、打开终端命令窗口;2、执行“mv A B”命令将目录A重命名为B,或者通过执行命令如“mv abc 1234”重命名指定文件;3、使用另一个“rename”命令实现重命名即可。 本教程操作环境:linux5.9.8系统、Dell G3电脑。 linux重命名文件怎么实…

    2026年10月9日 • 用户投稿
    000
  • Linus 将 Bcachefs 文件系统标记为由外部维护

    Linus 将 Bcachefs 文件系统标记为由外部维护Linus 将 Bcachefs 文件系统标记为由外部维护Linus 将 Bcachefs 文件系统标记为由外部维护Linus 将 Bcachefs 文件系统标记为由外部维护

    根据 Linux 内核的提交历史,Linus Torvalds 最近修改了内核中的 MAINTAINERS 文件,将 Bcachefs 文件系统的维护状态调整为“外部维护”。这一变动表明,他将不再接收针对 Bcachefs 的新功能补丁提交(PR),相当于关闭了该文件系统进入主线内核的更新通道。 此…

    2026年10月9日 • 用户投稿
    100
  • linux less命令怎么用

    linux less命令怎么用linux less命令怎么用linux less命令怎么用linux less命令怎么用

    linux less命令的作用是随意浏览文件,并且支持翻页和搜索,还支持向上翻页和向下翻页,其使用方法如“less log2013.log”表示查看“log2013.log”文件,“ps -ef |less”则表示通过ps查看进程信息并使用less分页显示。 本教程操作环境:linux5.9.8系统…

    2026年10月9日 • 用户投稿
    200
  • linux怎么共享文件夹

    linux怎么共享文件夹linux怎么共享文件夹linux怎么共享文件夹linux怎么共享文件夹

    linux共享文件夹的设置方法:1、通过“sudo apt-get install samba”命令安装samba;2、通过“mkdir urd360”命令创建了一个名为urd360的文件夹;3、点击该文件夹,右键选择properties,设置共享属性即可。 本教程操作环境:Ubuntu16.04系…

    2026年10月9日 • 用户投稿
    000
  • 文件夹的后缀名

    文件夹的后缀名文件夹的后缀名文件夹的后缀名文件夹的后缀名

    文件夹没有传统后缀名,因其类型由文件系统元数据标识,而非扩展名;如.git、.app等特殊命名实为命名约定或封装形式,并非真正后缀;系统通过属性识别文件夹,避免命名冲突与解析混乱,确保结构清晰。 文件夹通常是没有传统意义上的“后缀名”的。我们平时所说的后缀名,比如 .txt 、 .jpg 、 .ex…

    2026年10月9日 • 用户投稿
    600
  • linux怎么查找文件位置

    linux怎么查找文件位置linux怎么查找文件位置linux怎么查找文件位置linux怎么查找文件位置

    linux查找文件位置的方法:1、通过“whereis bash”命令查看可执行文件或源代码文件在文件系统中的位置;2、通过find命令在目录结构中搜索文件,并查找文件位置;3、使用which命令在PATH变量指定的路径中,搜索某个系统命令的位置。 本教程操作环境:linux5.9.8系统、Dell…

    2026年10月9日 • 用户投稿
    100
  • linux怎么查看主机名

    linux怎么查看主机名linux怎么查看主机名linux怎么查看主机名linux怎么查看主机名

    linux查看主机名的方法:1、通过hostnamectl命令查看系统主机名;2、使用hostname查看主机名;3、使用uname查看主机名;4、使用nmcli查看主机名;5、使用sysctl查看主机名;6、使用“cat /etc/hostname”查看主机名;7、使用“cat  /etc/hos…

    2026年10月9日 • 用户投稿
    100
  • linux明明有文件却找不到怎么办

    linux明明有文件却找不到怎么办linux明明有文件却找不到怎么办linux明明有文件却找不到怎么办linux明明有文件却找不到怎么办

    linux明明有文件却找不到的解决办法:1、打开终端命令窗口;2、通过执行“mv elasticsearch.yml elasticsearch.yml”命令查看文件即可。 本教程操作环境:linux5.9.8系统、Dell G3电脑。 linux明明有文件却找不到怎么办? Linux系统中文件明明…

    2026年10月9日 • 用户投稿
    200
  • Windows 上安装使用 Claude Code 指南

    Windows 上安装使用 Claude Code 指南Windows 上安装使用 Claude Code 指南Windows 上安装使用 Claude Code 指南Windows 上安装使用 Claude Code 指南

    大家好,我是 immerse,一位独立开发者兼内容创作者。 • 欢迎关注我的公众号:#沉浸式趣谈,获取最新文章(更多独家内容仅在公众号发布)• 个人网站: https://yaolifeng.com ,同步更新技术文章• 转载请务必在文首注明作者出处及版权信息 在这里,我会持续分享关于 编程 、 独…

    2026年10月9日 • 用户投稿
    100
  • linux查看内存大小的命令是什么

    linux查看内存大小的命令是什么linux查看内存大小的命令是什么linux查看内存大小的命令是什么linux查看内存大小的命令是什么

    linux查看内存大小的命令是“free”命令;通过“free”命令可以查看系统的内存使用情况,内存大小单位为KB;通过“free -m”命令查看系统的内存使用情况,内存大小单位为MB;通过“free -g”命令查看系统的内存使用情况,内存大小单位为GB。 本教程操作环境:linux5.9.8系统、…

    2026年10月9日 • 用户投稿
    100
  • [1039]linux使用sz命令下载大于4G的文件到windows

    [1039]linux使用sz命令下载大于4G的文件到windows[1039]linux使用sz命令下载大于4G的文件到windows[1039]linux使用sz命令下载大于4G的文件到windows[1039]linux使用sz命令下载大于4G的文件到windows

    在linux系统中使用 sz 命令下载大于4gb的文件到windows时,需要先将大文件拆分成多个小文件。以下是详细步骤和相关代码的伪原创版本: 拆分文件 在Linux中,使用以下命令将大文件拆分为2GB的小文件: cat tomcat7.zip | split -b 2G – tomcat7.zi…

    2026年10月9日 • 用户投稿
    1500
  • linux怎么查看网卡

    linux怎么查看网卡linux怎么查看网卡linux怎么查看网卡linux怎么查看网卡

    linux查看网卡的方法:1、通过“ifconfig”命令查看网络接口信息;2、通过“lspci |grep -i ‘eth’”或通过“lspci | grep -i net”命令查看网卡设备列表;3、通过“iwconfig”命令查看设备上是否有无线网卡设备;4、通过“eth…

    2026年10月9日 • 用户投稿
    100
  • linux如何查看目录占用空间大小

    linux如何查看目录占用空间大小linux如何查看目录占用空间大小linux如何查看目录占用空间大小linux如何查看目录占用空间大小

    在linux中,可以使用du命令查看目录占用空间大小,语法“du [选项] [目录或文件名]”;常用选项有“-a”显示当前目录和子目录/文件占用的磁盘空间大小,“-s”只显示当前目录占用的磁盘空间大小,“–apparent-size”显示目录或文件自身的大小,“-l”统计硬链接占用磁盘空…

    2026年10月9日 • 用户投稿
    900
  • composer archive命令如何排除特定文件或目录

    composer archive命令如何排除特定文件或目录composer archive命令如何排除特定文件或目录composer archive命令如何排除特定文件或目录composer archive命令如何排除特定文件或目录

    使用.gitattributes文件中的export-ignore规则可排除文件,如/tests、/docs、*.md等,保存后运行composer archive –format=zip –dir=build/即可生成不含指定内容的归档包。 使用 composer arch…

    2026年10月9日 • 用户投稿
    100
  • u盘检测真实容量软件

    u盘检测真实容量软件u盘检测真实容量软件u盘检测真实容量软件u盘检测真实容量软件

    U盘检测真实容量软件能识别虚假扩容盘,通过全盘读写测试验证实际存储能力,避免数据丢失。 U盘检测真实容量软件,顾名思义,就是用来识别U盘实际存储能力的工具。在数字世界里,我们常常遇到一些看似物美价廉的U盘,标着惊人的容量,比如1TB甚至2TB,但实际买回来一用,不是数据丢失就是文件损坏。这类软件的核…

    2026年10月9日 • 用户投稿
    500
  • linux挂载硬盘命令是mount命令吗

    linux挂载硬盘命令是mount命令吗linux挂载硬盘命令是mount命令吗linux挂载硬盘命令是mount命令吗linux挂载硬盘命令是mount命令吗

    linux挂载硬盘命令是mount命令。mount是挂载命令,可将分区挂载到文件夹下的,达到将分区和目录联系起来,以后只要访问这个文件夹就就相当于访问该分区,语法“mount [-t 系统类型] [-L 卷标名] [-o 特殊选项] [-n] 设备文件名 挂载点”。 本教程操作环境:linux7.3…

    2026年10月9日 • 用户投稿
    100
  • linux运行文件命令有哪些

    linux运行文件命令有哪些linux运行文件命令有哪些linux运行文件命令有哪些linux运行文件命令有哪些

    linux运行文件命令有:1、使用source执行文件,命令语法为“source 文件名”。2、利用“.”执行文件,命令语法为“.  文件名”。3、使用bash执行脚本文件,命令语法为“bash 文件名”。4、使用“./file”执行脚本文件,命令语法为“./文件名”;此种方式首先得给脚本添加执行权…

    2026年10月9日 • 用户投稿
    100
  • Windows上TXT文件打不开怎么办?解决文本文件编码问题的技巧

    Windows上TXT文件打不开怎么办?解决文本文件编码问题的技巧Windows上TXT文件打不开怎么办?解决文本文件编码问题的技巧Windows上TXT文件打不开怎么办?解决文本文件编码问题的技巧Windows上TXT文件打不开怎么办?解决文本文件编码问题的技巧

    TXT文件打不开或乱码通常由编码不匹配导致,可尝试用记事本、Notepad++等编辑器以UTF-8、GBK等不同编码重新打开或另存为,优先选择UTF-8编码保存,并养成使用专业工具检查和转换编码的习惯,以避免问题复发。 TXT文件打不开或者打开后内容显示为一堆乱码,这在Windows系统上是相当常见…

    2026年10月9日 • 用户投稿
    200
  • linux管道是什么

    linux管道是什么linux管道是什么linux管道是什么linux管道是什么

    在linux中,管道是一种通信机制,是把一个程序的输出直接连接到另一个程序的输入。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:限制管道的大小、读取进程可能工作得比写进程快。 本教程操作环境:linux7.3系统、Dell G3电脑。 …

    2026年10月9日 • 用户投稿
    000

发表回复

登录后才能评论
关注微信