0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux的进程

strongerHuang 来源:嵌入式Hacker 作者:吴伟东 2020-11-29 09:51 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

目的:

初步了解进程描述符 task_struct。

目录:

Linux的进程

Linux 的进程描述符

task_struct

内核如何找到 task_struct

task_struct 的分配和初始化

实验:打印 task_struct / thread_info / kernel mode stack

环境:

Linux-4.14 + ARMv7

1. Linux 的进程

进程的术语是 process,是 Linux 最基础的抽象,另一个基础抽象是文件。

最简单的理解,进程就是执行中 (executing, 不等于running) 的程序。

更准确一点的理解,进程包括执行中的程序以及相关的资源(包括cpu状态、打开的文件、挂起的信号、tty、内存地址空间等)。

一种简洁的说法:进程 = n*执行流 + 资源,n>=1。

Linux 进程的特点:

通过系统调用 fork() 创建进程,fork() 会复制现有进程来创建一个全新的进程。

内核里,并不严格区分进程和线程。

从内核的角度看,调度单位是线程 (即执行流)。可以把线程看做是进程里的一条执行流,1个进程里可以有1个或者多个线程。

内核里,常把进程称为 task 或者 thread,这样描述更准确,因为许多进程就只有1条执行流。

内核通过轻量级进程 (lightweight process) 来支持多线程。1个轻量级进程就对应1个线程,轻量级进程之间可以共享打开的文件、地址空间等资源。

2. Linux 的进程描述符

2.1 task_struct

内核里,通过 task_struct 结构体来描述一个进程,称为进程描述符 (process descriptor),它保存着支撑一个进程正常运行的所有信息。

每一个进程,即便是轻量级进程(即线程),都有1个 task_struct。

sched.h(includelinux) structtask_struct{ structthread_infothread_info; volatilelongstate; void*stack; [...] structmm_struct*mm; [...] pid_tpid; [...] structtask_struct*parent; [...] charcomm[TASK_COMM_LEN]; [...] structfiles_struct*files; [...] structsignal_struct*signal; }

这是一个庞大的结构体,不仅有许多进程相关的基础字段,还有许多指向其他数据结构的指针。

它包含的字段能完整地描述一个正在执行的程序,包括 cpu 状态、打开的文件、地址空间、挂起的信号、进程状态等。

点击查看大图

作为初学者,先简单地了解部分字段就好::

struct thread_info thread_info:进程底层信息,平台相关,下面会详细描述。

long state:进程当前的状态,下面是几个比较重要的进程状态以及它们之间的转换流程。

点击查看大图

void *stack:指向进程内核栈,下面会解释。

struct mm_struct *mm:与进程地址空间相关的信息都保存在一个叫内存描述符 (memory descriptor) 的结构体 (mm_struct) 中。

点击查看大图

pid_t pid:进程标识符,本质就是一个数字,是用户空间引用进程的唯一标识。

struct task_struct *parent:父进程的 task_struct。

char comm[TASK_COMM_LEN]:进程的名称。

struct files_struct*files:打开的文件表。

struct signal_struct *signal:信号处理相关。

其他字段,等到有需要的时候再回过头来学习。

2.2 当发生系统调用或者进程切换时,内核如何找到 task_struct ?

对于 ARM 架构,答案是:通过内核栈 (kernel mode stack)。

为什么要有内核栈?

因为内核是可重入的,在内核中会有多条与不同进程相关联的执行路径。因此不同的进程处于内核态时,都需要有自己私有的进程内核栈 (process kernel stack)。

当进程从用户态切换到内核态时,所使用的栈会从用户栈切换到内核栈。

至于是如何切换的,关键词是系统调用,这不是本文关注的重点,先放一边,学习内核要懂得恰当的时候忽略细节。

当发生进程切换时,也会切换到目标进程的内核栈。

同上,关键词是硬件上下文切换 (hardware context switch),忽略具体实现。

无论何时,只要进程处于内核态,就会有内核栈可以使用,否则系统就离崩溃不远了。

ARM 架构的内核栈和 task_struct 的关系如下:

内核栈的长度是 THREAD_SIZE,对于 ARM 架构,一般是 2 个页框的大小,即 8KB。

内核将一个较小的数据结构 thread_info 放在内核栈的底部,它负责将内核栈和 task_struct 串联起来。thread_info 是平台相关的,在 ARM 架构中的定义如下:

//thread_info.h(archarmincludeasm) structthread_info{ unsignedlongflags;/*lowlevelflags*/ intpreempt_count;/*0=>preemptable,<0 =>bug*/ mm_segment_taddr_limit;/*addresslimit*/ structtask_struct*task;/*maintaskstructure*/ [...] structcpu_context_savecpu_context;/*cpucontext*/ [...] };

thread_info 保存了一个进程能被调度执行的最底层信息(low level task data),例如struct cpu_context_save cpu_context 会在进程切换时用来保存/恢复寄存器上下文。

内核通过内核栈的栈指针可以快速地拿到 thread_info:

//thread_info.h(includelinux) staticinlinestructthread_info*current_thread_info(void) { //current_stack_pointer是当前进程内核栈的栈指针 return(structthread_info*) (current_stack_pointer&~(THREAD_SIZE-1)); }

然后通过 thread_info 找到 task_struct:

//current.h(includeasm-generic) #definecurrent(current_thread_info()->task)

内核里通过 current 宏可以获得当前进程的 task_struct。

2.3 task_struct 的分配和初始化

当上层应用使用 fork() 创建进程时,内核会新建一个 task_struct。

进程的创建是个复杂的工作,可以延伸出无数的细节。这里我们只是简单地了解一下 task_struct 的分配和部分初始化的流程。

fork() 在内核里的核心流程:

dup_task_struct() 做了什么?

至于设置内核栈里做了什么,涉及到了进程的创建与切换,不在本文的关注范围内,以后再研究了。

3. 实验:打印 task_struct / thread_info / kernel mode stack

实验目的:

梳理 task_struct / thread_info / kernel mode stack 的关系。

实验代码:

#include #include #include staticvoidprint_task_info(structtask_struct*task) { printk(KERN_NOTICE"%10s%5dtask_struct(%p)/stack(%p~%p)/thread_info->task(%p)", task->comm, task->pid, task, task->stack, ((unsignedlong*)task->stack)+THREAD_SIZE, task_thread_info(task)->task); } staticint__inittask_init(void) { structtask_struct*task=current; printk(KERN_INFO"taskmoduleinit "); print_task_info(task); do{ task=task->parent; print_task_info(task); }while(task->pid!=0); return0; } module_init(task_init); staticvoid__exittask_exit(void) { printk(KERN_INFO"taskmoduleexit "); } module_exit(task_exit);

运行效果:

taskmoduleinit insmod3123task_struct(edb42580)/stack(ed46c000~ed474000)/thread_info->task(edb42580) bash2393task_struct(eda13e80)/stack(c9dda000~c9de2000)/thread_info->task(eda13e80) sshd2255task_struct(ee5c9f40)/stack(c9d2e000~c9d36000)/thread_info->task(ee5c9f40) sshd543task_struct(ef15f080)/stack(ee554000~ee55c000)/thread_info->task(ef15f080) systemd1task_struct(ef058000)/stack(ef04c000~ef054000)/thread_info->task(ef058000)

在程序里,我们通过 task_struct 找到 stack,然后通过 stack 找到 thread_info,最后又通过 thread_info->task 找到 task_struct。

4. 相关参考

Linux 内核设计与实现 / 第 3.1 章节

深入理解 Linux 内核 / 3

Linux 内核深度解析 / 2.5.1

深入Linux 内核架构 / 2.3

责任编辑:lq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11627

    浏览量

    217892
  • 多线程
    +关注

    关注

    0

    文章

    279

    浏览量

    20911
  • 进程
    +关注

    关注

    0

    文章

    208

    浏览量

    14477

原文标题:Linux 内核 / 进程管理 / 如何描述一个进程?

文章出处:【微信号:strongerHuang,微信公众号:strongerHuang】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Linux驱动开发的必备知识

    内核基础知识: 1、熟悉 Linux 内核的架构、模块系统、进程管理、内存管理等。 了解内核的编译和加载过程。 2、C编程技能: 精通 C 语言编程,包括指针操作、内存管理、结构体等
    发表于 12-04 07:58

    Linux多线程对比单线程的优势

    Linux系统中,线程是操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中的实际运行单位。一个进程可以拥有多个线程,这些线程共享相同的内存空间和系统资源。线程主要解决
    发表于 12-01 06:11

    Linux进程间通信(IPC)全解析:从管道到 Socket,一篇讲透

    在 Linux 世界里,进程并非孤立存在。无论是后台服务协作(如 Web 服务器与数据库)、命令行工具联动(如ps | grep),还是复杂应用的模块通信,都离不开 进程间通信(IPC
    的头像 发表于 11-14 21:38 1.2w次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>进程</b>间通信(IPC)全解析:从管道到 Socket,一篇讲透

    【作品合集】合众HZ-T536开发板测评

    【HZ-T536开发板免费体验】—— linux 进程创建 【HZ-T536开发板免费体验】—— linux创建线程 作者:zealsoft【HZ-T536开发板免费体验】1 - 开箱测试 【HZ-T536开发板
    发表于 09-12 09:37

    【HZ-T536开发板免费体验】—— linux创建线程

    线程与进程 一个进程指的是一个正在执行的应用程序,而线程的功能是执行应用程序中的某个具体任务。线程具有传统线程的特征,一个进程包括多个线程,或者至少包括一个线程,所有线程共享进程的资源
    发表于 09-01 21:31

    【HZ-T536开发板免费体验】—— linux 进程创建

    Linux进程通信方式有这几种: 1。管道 2。信号量 3。消息队列 4。共享内存 在本帖子中,我会讲解fork(),exit()系统调用的实践。通过应用编程来实现系统调用。 1,进程创建 打开
    发表于 09-01 20:49

    详解Linux系统中的服务管理

    Linux中,无论何时当你安装任何带有服务和守护进程的包,系统默认会把这些服务的初始化及 systemd脚本添加进去,不过此时它们并没有被启用。
    的头像 发表于 05-23 15:10 619次阅读
    详解<b class='flag-5'>Linux</b>系统中的服务管理

    Linux常用命令大全

    Linux常用命令是指在Linux操作系统中广泛使用的命令工具,这些命令工具可以完成各种不同的任务,如管理文件和目录、操作进程、网络通信、软件安装等。
    的头像 发表于 05-03 18:08 1609次阅读

    Linux后台进程管理详解

    当我们在终端或控制台工作时,可能不希望由于运行一个作业而占住了屏幕,因为可能还有更重要的事情要做,比如阅读电子邮件。对于密集访问磁盘的进程,我们更希望它能够在每天的非负荷高峰时间段运行(例如凌晨)。为了使这些进程能够在后台运行,也就是说不在终端屏幕上运行,有几种选择方法可
    的头像 发表于 04-25 11:04 796次阅读
    <b class='flag-5'>Linux</b>后台<b class='flag-5'>进程</b>管理详解

    Linux系统进程管理入门指南

    Linux 系统中,进程是正在运行的程序的实例。理解进程的管理、查看和控制对于系统管理员和开发者来说非常重要
    的头像 发表于 04-22 14:34 852次阅读
    <b class='flag-5'>Linux</b>系统<b class='flag-5'>进程</b>管理入门指南

    Linux网络设置与基础服务

    Linux网络设置与基础服务
    的头像 发表于 04-09 11:51 3136次阅读
    <b class='flag-5'>Linux</b>网络设置与基础服务

    Linux进程状态详解

    进程状态是task_struct内的一个整数;进行:进程在调度队列中,进程的状态都是running,阻塞:等待某种设备或者资源就绪。进程是一个队列,设备也是一个队列,当我们读磁盘,读网
    的头像 发表于 04-01 09:46 803次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>进程</b>状态详解

    Linux计划任务cron详解

    cron是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,crond
    的头像 发表于 02-07 15:31 1525次阅读
    <b class='flag-5'>Linux</b>计划任务cron详解

    深入解析Linux程序与进程

    什么是程序 一组计算机能识别和执行的指令,用于指导计算机执行特定任务或解决特定问题。程序通常由代码、数据和资源文件组成,涉及语法、算法和数据结构。为二进制文件 什么是进程 是一个具有独立功能的程序
    的头像 发表于 12-18 11:01 829次阅读
    深入解析<b class='flag-5'>Linux</b>程序与<b class='flag-5'>进程</b>

    Linux之CPU调度策略和CPU亲和性

    一、调度策略 调度进程 单个 CPU一次只能执行一个进程,虽然 Linux 系统通过使用多任务同时处理多个进程,但当多个进程同时运行在一个C
    的头像 发表于 12-05 16:38 1666次阅读
    <b class='flag-5'>Linux</b>之CPU调度策略和CPU亲和性