Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。因为内核线程只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。

内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。

创建内核线程最基本的两个接口函数是:

kthread_run(threadfn, data, namefmt, ...)

kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)

这里我们主要介绍kthread_run,后面会专门分析这两个函数的异同。

kthread_run 事实上是一个宏定义:

/*** 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;                                               \})

kthread_run()负责内核线程的创建,它由kthread_create()和wake_up_process()两部分组成,这样的好处是用kthread_run()创建的线程可以直接运行。外界调用kthread_run创建运行线程。kthread_run是个宏定义,首先调用kthread_create()创建线程,如果创建成功,再调用wake_up_process()唤醒新创建的线程。kthread_create()根据参数向kthread_create_list中发送一个请求,并唤醒kthreadd,之后会调用wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用schedule()调度出去。kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。

在介绍完如何创建线程之后,下面来介绍另外两个基本的函数:

int kthread_stop(struct task_struct *k);

int kthread_should_stop(void);

kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志should_stop,并等待线程主动结束,返回线程的返回值。在调用 kthread_stop()结束线程之前一定要检查该线程是否还在运行(通过 kthread_run 返回的 task_stuct 是否有效),否则会造成灾难性的后果。kthread_run的返回值tsk。不能用tsk是否为NULL进行检查,而要用IS_ERR()宏定义检查,这是因为返回的是错误码,大致从0xfffff000~0xffffffff。

kthread_should_stop()返回should_stop标志(参见 struct kthread )。它用于创建的线程检查结束标志,并决定是否退出。

kthread() (注:原型为:static int kthread(void *_create) )的实现在kernel/kthread.c中,头文件是include/linux/kthread.h。内核中一直运行一个线程kthreadd,它运行kthread.c中的kthreadd函数。在kthreadd()中,不断检查一个kthread_create_list链表。kthread_create_list中的每个节点都是一个创建内核线程的请求,kthreadd()发现链表不为空,就将其第一个节点退出链表,并调用create_kthread()创建相应的线程。create_kthread()则进一步调用更深层的kernel_thread()创建线程,入口函数设在kthread()中。

外界调用kthread_stop()删除线程。kthread_stop首先设置结束标志should_stop,然后调用wake_for_completion(&kthread->exited)上,这个其实是新线程task_struct上的vfork_done,会在线程结束调用do_exit()时设置。

附:

struct kthread {int should_stop;struct completion exited;};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_HIGH_MEMORY]);current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;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;
}/*** kthread_stop - stop a thread created by kthread_create().* @k: thread created by kthread_create().** Sets kthread_should_stop() for @k to return true, wakes it, and* waits for it to exit. This can also be called after kthread_create()* instead of calling wake_up_process(): the thread will exit without* calling threadfn().** If threadfn() may call do_exit() itself, the caller must ensure* task_struct can't go away.** Returns the result of threadfn(), or %-EINTR if wake_up_process()* was never called.*/int kthread_stop(struct task_struct *k)
{struct kthread *kthread;int ret;trace_sched_kthread_stop(k);get_task_struct(k);kthread = to_kthread(k);barrier(); /* it might have exited */if (k->vfork_done != NULL) {kthread->should_stop = 1;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;}

Linux内核多线程(二)相关推荐

  1. 笔记:深入理解Linux内核(二)

    笔记:深入理解Linux内核(二) 二零二一年十月二十四日 文章目录 笔记:深入理解Linux内核(二) 第二章:内存寻址 内存地址 硬件中的分段 段选择符和段选择器 段描述符 快速访问段描述符 分段 ...

  2. Linux内核多线程实现方法 —— kthread_create函数

          Linux内核多线程实现方法 -- kthread_create函数 内核经常需要在后台执行一些操作,这种任务就可以通过内核线程(kernle thread)完成独立运行在内核空间的标准进 ...

  3. 从零开始学Linux内核驱动--(二)简单内核模块驱动程序

    Linux驱动–(二)简单的内核模块驱动程序 一.概述 Linux中所有的驱动都是以内核模块的形式来实现的,他们与其他所有的内核编译在一起形成一个单独的内核镜像文件(所以说Linux是一个宏内核).当 ...

  4. linux内核(二)内核移植(DM365-DM368开发攻略——linux-2.6.32的移植)

    一.介绍linux-2.6.32: Linux-2.6.32的网上介绍:增添了虚拟化内存 de-duplicacion.重写了 writeback 代码.改进了 Btrfs 文件系统.添加了 ATI ...

  5. Linux内核分析(二) 段页式分析

    80386CPU 页式存储管理机制 关于80386段式管理,也是Linux内核采用的段式管理部分已在硬件平台分析给出了详细分析(段式存储管理). 接着我们详细分析建立在段机制上的 80386CPU 页 ...

  6. Linux内核 实践二

    实践二 内核模块编译 20135307 张嘉琪 一.实验原理 Linux模块是一些可以作为独立程序来编译的函数和数据类型的集合.之所以提供模块机制,是因为Linux本身是一个单内核.单内核由于所有内容 ...

  7. Linux 内核调试 二:ubuntu20.04安装qemu

    一.第一种方法 使用 apt-get install qemu 命令进行安装,安装过程如下: onlylove@ubuntu:~$ sudo apt-get install qemu Reading ...

  8. Linux内核实战(二)-操作系统概述

    os就像一个软件外包,内核就相当于这家外包公司老板. 接下来请假设你就是这个老板,方便理解os如何协调各种资源帮客户做事. 后文中 用户指os的用户 客户指外包公司的客户 1 硬件概述 1.1 鼠标和 ...

  9. Linux内核配置(二) :CPU类型配置

    5. Processor type and features 处理器类型及特性 5.1. Symmetric multi-processing support (SMP) 对称多处理器支持. 这将支持 ...

  10. linux内核笔记二 进程管理

    三.系统的进程管理 1.系统的进程运转方式 系统时间 (jiffies 系统滴答) cpu内部有一个RTC(系统的定时器),会在上电的时候调用mktime函数算出1970年一月一日0时开始到当前开机点 ...

最新文章

  1. mysql防注入pdo_mysql PDO和存储过程动态SQL注入
  2. ubuntu 安装 anaconda
  3. 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
  4. QML资源加载和网络透明度
  5. Qt::WindowType、Qt::WidgetAttribute各个标志含义汇总
  6. 第三次大转型:中国居住新十年报告
  7. Python zip() 函数--多个迭代器取元素组合成一个新的迭代器
  8. C#LeetCode刷题之#682-棒球比赛(Baseball Game)
  9. Java 9 ← 2017,2019 → Java 13 ,Java 两年来都经历了什么?| CSDN 博文精选
  10. List的三种遍历方式
  11. 【软考10】计算机网络基础知识拾遗
  12. 2021面试题——CSS面试题总结
  13. ASP.NET实现日期转为大写的汉字
  14. 前端开发工程师需要的技能
  15. Required Remainder
  16. 游戏服务器开发技术小结
  17. 图片如何添加水印文字?如何设置照片水印?
  18. 嵌入式基础01【转载】详解大端模式和小端模式
  19. 巨大金中华,烽火普天下
  20. KB/S MBPS转换

热门文章

  1. 博文内部设置目录尝试
  2. PHP中strtotime()的使用
  3. python基础之socket
  4. asp.net gridview添加删除确认对话框
  5. 2015-2016-2 《Java程序设计》项目小组博客
  6. OA报账规范(出差专用)
  7. php中数组自定义排序
  8. Xtreme Report为windows开发者提供一个完善的类似于Outlook 2003报表的风格
  9. 利用内存分析工具(Memory Analyzer Tool,MAT)分析java项目内存泄露
  10. idea module重命名后去掉后面带着的原来的名字