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声明式事务的配置陷阱与正确使用方案
上一篇 2025年11月4日 06:18:53
win10系统cf连接服务器失败怎么办?
下一篇 2025年11月4日 06:19:01

相关推荐

  • Java javac 命令与当前工作目录解析

    在Java编译环境中,javac命令的“当前目录”指的是命令被执行的物理位置,而非源文件所在的目录。理解这一概念对于正确配置和管理Java项目的编译路径至关重要,特别是当默认的classpath设置为.时,它决定了编译器查找类文件的起点。 1. javac 命令与当前工作目录的定义 在操作系统中,当…

    2026年9月23日
    100
  • Bash Shell 中单引号和双引号的区别

    Bash Shell 中单引号和双引号的区别Bash Shell 中单引号和双引号的区别Bash Shell 中单引号和双引号的区别Bash Shell 中单引号和双引号的区别

    在 linux 命令行中,引号是处理文件名中的空格和特殊字符的常用工具。引号在 shell 脚本中具有“特殊功能”,可能让初学者感到困惑。让我们详细探讨不同类型的引号字符及其在 shell 脚本中的用法。 有四种不同类型的引号字符: 单引号 ‘双引号 “反斜杠 反引号 ` 除…

    2026年9月23日 用户投稿
    500
  • Linux中如何查看服务日志?journalctl与syslog使用指南

    Linux中如何查看服务日志?journalctl与syslog使用指南Linux中如何查看服务日志?journalctl与syslog使用指南Linux中如何查看服务日志?journalctl与syslog使用指南Linux中如何查看服务日志?journalctl与syslog使用指南

    排查linux服务问题时,首选journalctl或syslog类系统查看日志。journalctl适用于systemd系统,可查看内核消息、服务启动输出等,支持按时间、单元、优先级过滤;syslog适用于传统系统,需服务主动发送日志,支持集中管理。掌握两者使用能有效定位问题。 在Linux系统中排…

    2026年9月23日 用户投稿
    100
  • Reflection AI 完成 20 亿美元融资,打造“开放智能”

    美国人工智能初创企业 reflection ai 宣布成功募集 20 亿美元资金,其中英伟达领衔投资 8 亿美元,推动公司估值跃升至 80 亿美元。这家成立仅一年的科技新星,致力于打造“人人可及的前沿开放智能(open intelligence)”。 Reflection AI 表示,已集结一支由顶…

    2026年9月23日
    500
  • [272]如何把Python脚本导出为exe程序

    [272]如何把Python脚本导出为exe程序[272]如何把Python脚本导出为exe程序[272]如何把Python脚本导出为exe程序[272]如何把Python脚本导出为exe程序

    文章目录:一. PyInstaller简介二. PyInstaller在Windows下的安装三. 打包四. 小实例(Windows下) 附加:pyinstaller简介 PyInstaller能够将Python脚本打包成可执行程序,使得在没有Python环境的机器上也可以运行这些程序。 PyIns…

    2026年9月23日 用户投稿
    100
  • VSCode搭建Flutter开发环境(移动开发,完整配置指南)

    本文详细指导如何在VSCode中搭建高效的Flutter开发环境,包括安装JDK、配置JAVA_HOME、安装Android Studio并设置ANDROID_HOME、安装VSCode及Flutter和Dart插件、配置FLUTTER_HOME环境变量,通过flutter doctor检查并解决A…

    2026年9月23日
    100
  • 渗透测试|利用curl回传文件

    在处理低权限shell回传文件的问题时,如果无法使用scp命令且无法安装sshpass,可以考虑使用curl命令进行文件传输。以下是详细的伪原创内容: 至少我们曾经在一起过。 来自:一言 var xhr = new XMLHttpRequest();xhr.open(‘get’, ‘https://…

    2026年9月23日
    200
  • VSCode如何配置Scala开发环境 VSCode搭建Scala项目的完整教程

    首先安装jdk 11或17并正确配置java_home和path环境变量;2. 通过包管理器或官网安装sbt,用于项目构建与依赖管理;3. 在vscode中安装scala (metals)插件,以获得代码补全、错误检查等语言服务;4. 使用sbt new scala/scala-seed.g8创建项…

    2026年9月23日
    100
  • 无代码调整聚类热图分支顺序

    无代码调整聚类热图分支顺序无代码调整聚类热图分支顺序无代码调整聚类热图分支顺序无代码调整聚类热图分支顺序

    聚类热图的分支结构会因不同的聚类算法和距离计算方式而有所不同。在保持分支结构不变的前提下,我们有时需要调整热图分支的顺序,以满足特定的需求。这就是文章聚类热图如何按自己的意愿调整分支顺序的出发点。 现在,这种功能已经移植到BIC平台,具体操作如下: 使用之前的绘图数据: 采用默认的绘图参数: 生成的…

    2026年9月23日 用户投稿
    300
  • Java中ConnectException连接异常的解决方法

    答案:Java中ConnectException通常因服务未启动、网络不通或配置错误导致,需检查服务状态、IP端口配置及防火墙设置,并合理设置连接超时与重试机制。 Java中出现ConnectException通常表示应用程序尝试连接到远程服务器时失败,最常见的原因是目标主机拒绝连接或网络不通。这个…

    2026年9月23日
    200
  • VSCode高效配置Elixir:Phoenix框架、中文提示、模式匹配

    要高效配置vscode支持elixir开发,必须安装elixirls扩展并确保elixir和erlang环境正确;elixirls提供代码补全、跳转、格式化和调试功能,配合手动设置.heex、.leex文件关联为html可优化phoenix框架开发体验;通过安装中文语言包、设置files.encod…

    2026年9月23日
    200
  • 鸿蒙3.0将删除谷歌代码,只是为让国产系统更纯粹

    鸿蒙3.0将删除谷歌代码,只是为让国产系统更纯粹鸿蒙3.0将删除谷歌代码,只是为让国产系统更纯粹鸿蒙3.0将删除谷歌代码,只是为让国产系统更纯粹鸿蒙3.0将删除谷歌代码,只是为让国产系统更纯粹

    作为“聚光灯下诞生的国产系统”,华为鸿蒙系统自诞生之日起就引发了激烈的争论。尽管鸿蒙系统已升级至3.0版本,但关于“鸿蒙系统是否是安卓套壳”的讨论依然是焦点。不过,这可能并不是问题的核心。 鸿蒙系统是套壳吗?对于如今的国内科技企业来说,开发一个系统并不困难。然而,为什么最终存活下来的只有MIUI、F…

    2026年9月23日 用户投稿
    100
  • 分享一个服务器一键测试脚本

    superbench是一个测试linux服务器性能的脚本,让你快速方便了解一台服务器的综合性能,支持硬件基本信息、流媒体解锁检测、磁盘io检测、cpu性能测试、以及国内和国外网络测速、网络路由追踪。 以上就是分享一个服务器一键测试脚本的详细内容,更多请关注创想鸟其它相关文章!

    2026年9月22日
    200
  • Linus Torvalds 批评 Rust 代码格式化工具:称其“完全疯狂”

    近日,linux创始人linus torvalds在linux内核邮件列表中对rust语言的代码格式化工具rustfmt提出了尖锐批评,称其行为“完全疯狂”。 他提到,在Rust代码中,类似use crate::xyz;这样的导入语句,在经过自动格式化后经常被合并为一行,导致原本清晰的结构变得混乱,…

    用户投稿 2026年9月22日
    300
  • 解决TCPDF保存文件权限问题的完整指南

    本文旨在解决使用tcpdf在%ignore_a_1%中生成pdf并保存到服务器(’f’模式)时遇到的“permission denied”错误,尤其是在macos环境下。核心问题通常源于不正确的服务器文件路径或目标文件夹缺乏写入权限。教程将详细阐述如何构建正确的绝对文件路径,…

    2026年9月22日
    300
  • LINUX如何安全地关闭系统_LINUX正确的关机与重启命令

    使用shutdown命令可安全关机,如sudo shutdown -h now立即关机,或+5表示5分钟后关机,-c取消计划;2. poweroff立即断电;3. halt停止系统运行,加–poweroff关闭电源;4. reboot重启系统,也可用shutdown -r +3延迟重启;…

    2026年9月22日
    100
  • MySQL服务无法启动怎么办?常见解决方法

    MySQL服务无法启动怎么办?常见解决方法MySQL服务无法启动怎么办?常见解决方法MySQL服务无法启动怎么办?常见解决方法MySQL服务无法启动怎么办?常见解决方法

    mysql服务无法启动常见原因包括配置错误、端口占用、数据文件损坏或权限问题。解决方法如下:1. 查看错误日志,定位问题根源;2. 检查配置文件是否存在语法错误或路径问题;3. 确认端口(如3306)未被占用;4. 核查数据目录的权限与完整性;5. 必要时修复或重置数据目录,甚至重新安装mysql。…

    2026年9月22日 用户投稿
    100
  • Linux内核13-进程切换

    进程切换,也称为任务切换、上下文切换或任务调度,本文将探讨linux内核中进程切换的实现。我们首先理解几个关键概念。 1.1 硬件上下文 每个进程都有自己的地址空间,但所有进程共享CPU寄存器。因此,在恢复进程执行前,内核必须确保挂起时的寄存器值被重新加载到CPU寄存器中。 这些需要加载到CPU寄存…

    2026年9月22日
    300
  • 如何修改MySQL的默认端口号?

    如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?

    修改mysql默认端口号需编辑配置文件,核心步骤为:1.定位my.cnf或my.ini文件;2.在[mysqld]段落中修改或添加port参数;3.保存后重启mysql服务。更改端口主要出于避免冲突、提升安全性和适应网络策略考虑。连接时需在客户端工具或代码中指定新端口,如命令行加-p参数、编程语言连…

    2026年9月22日 用户投稿
    1300
  • 在 Linux 中如何强制停止进程?kill 和 killall 命令有什么区别?

    在日常工作中,您可能会遇到两个用于在 linux 中强制结束程序的命令:kill和killall。虽然许多 linux 用户熟悉kill命令,但使用killall命令的人相对较少。尽管这两个命令名称相似且目的相同(终止进程),但它们在使用方式和效果上有显著区别。 那么,kill和killall之间有…

    2026年9月22日
    200

发表回复

登录后才能评论
关注微信