Linux源码-进程描述符

Linux操作系统引入了PCB(Process Control Block,进程控制块)结构。PCB是Linux操作系统识别进程的通道。

创建进程时,首先会创建PCB,根据PCB中的信息对进程实施有效管理。当进程终止后,Linux操作系统会释放对应的PCB资源。

PCB的数据结构是struct task_struct。包含四个方面,描述信息、控制信息、CPU上下文和资源管理。

task_struct位于源码<include/linux/sched.h>

首行的代码注释如下,sched.h用于定义task_struct结构体并提供主要的调度API,比如schedule(), wakeup等

/*

 * Define 'struct task_struct' and provide the main scheduler

 * APIs (schedule(), wakeup variants, etc.)

 */

task_struct总计有约700行代码。

1 描述信息

1.1 进程描述符

每个进程都有唯一的进程标识符pid。是一个32位正整型数。即单个系统内pid的上限为2的31次方*(2147483648),约21亿个PID

struct task_struct{

    ...(796行)

    pid_t               pid;  //全局进程号

    pid_t               tgid; //全局线程组标识符

    ...

}

1.2 用户标识符

每个进程都属于系统中的某一个用户,因此在数据结构体中会包含用户uid信息,变量为kuid_t

struct task_struct{

    ...(940行)

    kuid_t              loginuid;

    unsigned int            sessionid;

    ...

}

kuid_t在<include/linux/uidgid.h>中有定义,为val变量,是一个无符号整数,代表用户标识号。

typedef struct {

    uid_t val;

} kuid_t;

1.3 家族关系

进程并不会独立存在,互相之间存在依赖性,会形成依赖关系,主要为父子关系。除了0号进程之外,其他进程都有父进程。

父进程的父进程叫做祖先进程(隔代),同一个父进程中的多个子进程之间构成兄弟关系(同代)。

Linux系统启动时,0号进程进行内核初始化工作,并创建出1号进程。

1号进程完成用户空间初始化后成为init进程,为后续该系统内创建进程的共同祖先进程。

如下代码中real_parent和parent在正常情况下都指向父进程pid,但是在特殊情况下,比如因为程序缺陷,父进程在子进程结束前自行退出了,子进程就变成了孤儿进程,也就是僵尸进程,此时,该子进程就会被挂到init 1号进程下。这是real_parent不会变化,但是parent变成了1。

sibling用于指向兄弟进程。

children用于指向子进程。

struct task_struct{

    ...(809行)

    /*

     * Pointers to the (original) parent process, youngest child, younger sibling,

     * older sibling, respectively.  (p->father can be replaced with

     * p->real_parent->pid)

     */

    /* Real parent process: */

    struct task_struct __rcu    *real_parent; //指向真实的父进程

    /* Recipient of SIGCHLD, wait4() reports: */

    struct task_struct __rcu    *parent; //指向父进程

    /*

     * Children/sibling form the list of natural children:

     */

    struct list_head        children;  //指向子进程

    struct list_head        sibling;   //指向兄弟进程

    struct task_struct      *group_leader;  //通过进程描述符,指向线程组的组长

2控制信息

PCB中进程控制信息主要包括进程的状态信息、优先级信息和记账信息。

2.1 状态信息

进程的状态有就绪、运行、阻塞、终止等状态,其中在终止状态中又分为了僵尸和死亡两种状态。

内核代码中对进程状态有如下几种类型定义:

<include/linux/sched.h>

....(82行)

/* Used in tsk->state: */

#define TASK_RUNNING            0x0000 //运行状态,int 0

#define TASK_INTERRUPTIBLE      0x0001 //阻塞状态(可中断) int 1

#define TASK_UNINTERRUPTIBLE        0x0002 //阻塞状态(不可中断) int 2

#define __TASK_STOPPED          0x0004 //终止状态 int 4

#define __TASK_TRACED           0x0008 // 跟踪状态 int 8

/* Used in tsk->exit_state: */

#define EXIT_DEAD           0x0010 //终止状态中的死亡状态,属于exit_state

#define EXIT_ZOMBIE         0x0020//终止状态中的僵尸状态,属于exit_state

#define EXIT_TRACE          (EXIT_ZOMBIE | EXIT_DEAD)

在操作系统上可以看到的进程的状态描述:

static const char * const task_state_array[] = {

    /* states in TASK_REPORT: */

    "R (running)",      /* 0x00 */

    "S (sleeping)",     /* 0x01 */

    "D (disk sleep)",   /* 0x02 */

    "T (stopped)",      /* 0x04 */

    "t (tracing stop)", /* 0x08 */

    "X (dead)",     /* 0x10 */

    "Z (zombie)",       /* 0x20 */

    "P (parked)",       /* 0x40 */

    /* states beyond TASK_REPORT: */

    "I (idle)",     /* 0x80 */

};

定义一个宏TASK_REPORT, 将所有进程的状态进行合并。

/* get_task_state(): */

#define TASK_REPORT         (TASK_RUNNING | TASK_INTERRUPTIBLE | \

                     TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \

                     __TASK_TRACED | EXIT_DEAD | EXIT_ZOMBIE | \

                     TASK_PARKED)

task_state_index为内联函数,用于计算给定任务结构体task_struct的状态索引,明确一个进程的具体状态。

根据给定任务的运行状态 (tsk->state) 和退出状态 (tsk->exit_state),计算出一个状态索引。通过按位操作和掩码 (TASK_REPORT),函数能够准确地表示任务的当前状态,并返回一个索引值,用于进一步的处理或决策。

static inline unsigned int task_state_index(struct task_struct *tsk)

{

    unsigned int tsk_state = READ_ONCE(tsk->state);

    unsigned int state = (tsk_state | tsk->exit_state) & TASK_REPORT;

    BUILD_BUG_ON_NOT_POWER_OF_2(TASK_REPORT_MAX);

    if (tsk_state == TASK_IDLE)

        state = TASK_REPORT_IDLE;

    return fls(state);

}

2.2 进程优先级信息

进程优先级用于确定进程被调度到CPU上执行的优先程度如内核代码定义分为动态优先级、静态优先级、普通优先级和试试优先级

  struct task_struct {

......(732行)

    int             prio;         //动态优先级

    int             static_prio;  //静态优先级

    int             normal_prio;  //普通优先级,取决于静态优先级和调度策略

    unsigned int            rt_priority; //realtime,实时优先级

静态优先级:数值越小,优先级越高,可用nice调整

动态优先级和普通优先级默认等于静态优先级,但动态优先级会被临时修改。同样是数值越小,优先级越高。

进程可分为实时进程和普通进程,实时优先级作用于实时进程,数值越大,优先级越高。

实时进程的调度优先于普通进程。

3 CPU上下文

CPU上下文是指进程执行到某时刻时CPU各寄存器中的数值,这些数值就代表着当前进程活动的状态信息。

什么是上下文切换?

在单CPU多进程并发的场景下,单位时间内一个CPU同一时刻只会执行一个进程的代码,比如在1秒中内,CPU的时间周期被切割成100个分片,同时有10个进程需要执行,且每个进程之间的优先级都是相同的情况下,每个进程被分配到的时间就在10个时间分片,同时这10个时间分片不一定是连续的。因此在CPU进行两个进程处理切换期间,需要保留上一个进程在CPU内寄存器的数值,同时要加载下一个进程在上一个时间片内执行的寄存器数值。这个过程叫做上下文切换。

进程切换时,CPU上下文保存调用为task_struct --> thread_struct --> cpu_context

结构体 thread_struct,用于存储特定于进程的状态信息,特别是在处理器上下文、浮点寄存器状态和调试信息方面的详细数据。以下是结构体的主要成员及其作用的解释:

<arch/arm64/include/asm/processor.h>

...

struct thread_struct {

    struct cpu_context  cpu_context;    /* cpu context */

...

    unsigned long       fault_address;  /* fault info */

    unsigned long       fault_code; /* 寄存器ESR_EL1 value */

    struct debug_info   debug;      /* debugging */

    ...

    }

对应引用cpu_context结构体,主要代码如下,包含了通用寄存器x19--x28, 栈帧寄存器FP,堆栈指针寄存器SP和程序计数器PC

<arch/arm64/include/asm/processor.h>

struct cpu_context {

    unsigned long x19;

    unsigned long x20;

    unsigned long x21;

    unsigned long x22;

    unsigned long x23;

    unsigned long x24;

    unsigned long x25;

    unsigned long x26;

    unsigned long x27;

    unsigned long x28;

    unsigned long fp;

    unsigned long sp;

    unsigned long pc;

};

4 资源管理信息

PCB中包含资源管理信息,包含存储器、文件系统等等,多个代码调用关系如下

在sched.h中定义了对应的内存、文件、打开文件的结构体。

    <include/linux/sched.h>

    ...

    void *stack; //指向进程的内核栈

    ...

    struct mm_struct        *mm;

    struct mm_struct        *active_mm; //进程的用户控件描述符

    struct fs_struct        *fs;  //进程相关联的文件系统信息

    struct files_struct     *files;  //指向打开的文件列表

4.1 mm_types.h

mm_struct结构体记录了进程的内存布局

<include/linux/mm_types.h>

struct mm_struct {  //内存描述符

    struct {

        struct vm_area_struct *mmap;        /* list of VMAs */

        struct rb_root mm_rb;

        ...

        spinlock_t arg_lock; //自旋锁,保护如下字段

        //内存空间中各段起始、结束地址

         //包括堆、栈 、映射段、BSS段、代码段、数据段等等

        unsigned long start_code, end_code, start_data, end_data;       

        unsigned long start_brk, brk, start_stack;

        unsigned long arg_start, arg_end, env_start, env_end;

        ...

4.2 fs_struct.h

fs_struct结构体记录与进程相关联的文件系统信息,包括当前目录和根目录。

<include/linux/fs_struct.h>

struct fs_struct {

    int users;

    spinlock_t lock;  //自旋锁

    seqcount_spinlock_t seq;

    int umask;

    int in_exec;

    //根目录与当前目录

    struct path root, pwd;

} __randomize_layout;

通过t内联函数get_fs_roo获取根目录信息

通过内联函数get_fs_pwd获取当前目录信息

//通过get_fs_root内联函数获取根目录信息

static inline void get_fs_root(struct fs_struct *fs, struct path *root)

{

    spin_lock(&fs->lock);

    *root = fs->root;

    path_get(root);

    spin_unlock(&fs->lock);

}

//通过内联函数get_fs_pwd获取当前目录信息

static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd)

{

    spin_lock(&fs->lock);

    *pwd = fs->pwd;

    path_get(pwd);

    spin_unlock(&fs->lock);

}

4.3 fdtable.h

files_struct结构是进程正打开的所有文件的列表(包含输入/输出设备也是以文件形式存在的)

<include/linux/fdtable.h>

struct files_struct {

    atomic_t count;   //引用计数器

    bool resize_in_progress;

    wait_queue_head_t resize_wait;

    struct fdtable __rcu *fdt; //默认指向fdtab,用于动态申请内存

    struct fdtable fdtab;  //为fdt提供初始值

    spinlock_t file_lock ____cacheline_aligned_in_smp;

    unsigned int next_fd;

    unsigned long close_on_exec_init[1];

    unsigned long open_fds_init[1];

    unsigned long full_fds_bits_init[1];

    struct file __rcu * fd_array[NR_OPEN_DEFAULT];

    struct files_cgroup *files_cgroup;

};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/863126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2022年第十三届蓝桥杯比赛Java B组 【全部真题答案解析-第二部分】

上一篇文章&#xff1a;2022年第十三届蓝桥杯比赛Java B组 【全部真题答案解析-第一部分】_尘封的CPU的博客-CSDN博客最近回顾了Java B组的试题&#xff0c;深有感触&#xff1a;脑子长时间不用会锈住&#xff0c;很可怕。兄弟们&#xff0c;都给我从被窝里爬起来&#xff0c;赶…

解决error Error: certificate has expired问题

安装环境遇到下面问题&#xff1a; 产生原因&#xff1a;可能是开了服务器代理访问导致ssl安全证书失效 解决办法&#xff1a; 在终端输入以下命令&#xff1a; yarn config set "strict-ssl" false -g

3ds Max导出fbx贴图问题简单记录

1.前言 工作中发现3ds Max导出的fbx在其它软件&#xff08;Autodesk viewer&#xff0c;blender&#xff0c;navisworks&#xff0c;FBXReview等&#xff09;中丢失了部分贴图&#xff0c;但导出的fbx用3ds Max打开却正常显示。 fbx格式使用范围较广&#xff0c;很多常见的三…

Milvus【部署 01】向量数据库Milvus在Linux环境下的在线+离线安装

向量数据库Milvus在Linux环境下的在线离线安装 1.千问简介2.在线安装2.离线安装 1.千问简介 Milvus 是一款专为处理高维向量数据设计的开源云原生数据库&#xff0c;旨在满足海量向量数据的实时召回需求。它由 Zilliz 公司开发并维护&#xff0c;基于Apache许可证2.0版本发布。…

ASUS华硕A豆14笔记本电脑I421EAYB,I421EQYB_ADOL14EA工厂模式原厂Win11系统安装包下载

适用型号&#xff1a;ADOL14EA笔记本I421EAYB、I421EQYB 链接&#xff1a;https://pan.baidu.com/s/1krU8m_lbApyUfZQo5E4cCQ?pwd0ewl 提取码&#xff1a;0ewl 华硕原装WIN11系统工厂安装包&#xff0c;带有MyASUS WinRE RECOVERY恢复功能、自带所有驱动、出厂主题壁纸、系…

Linux上安装JDK,Java起飞,开启编程之旅

在Linux环境下搭建Java开发平台&#xff0c;是许多开发者入门Java世界的第一步。Java的跨平台特性使得在Linux上安装JDK尤为重要&#xff0c;它不仅能为Linux开发者提供更广阔的应用场景&#xff0c;也是探索Java强大功能的起点。 接下来&#xff0c;让我们一起在Linux中完成J…

【SpringMVC】_SpringMVC实现留言墙

目录 1. 需求分析 2. 接口定义 2.1 提交留言 2.2 获取全部留言 3. 响应数据 4. 服务器代码 4.1 MessageInfo 文件 4.2 MessageController 文件 5. 前端页面代码 5. 运行测试 1. 需求分析 实现如下页面&#xff1a; 1、输入留言信息&#xff0c;点击提交后&#xff0…

使用腾讯云服务器从0搭建个人网站,超简单图文教程

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网txyfwq.com整理使用腾讯云服务器建站教程&#xff0c;…

NPOI入门指南:轻松操作Excel文件的.NET库

目录 引言 一、NPOI概述 二、NPOI的主要用途 三、安装NPOI库 四、NPOI基本使用 六、性能优化和内存管理 七、常见问题与解决方案 八、结论 附录 引言 Excel文件作为数据处理的重要工具&#xff0c;广泛应用于各种场景。然而&#xff0c;在没有安装Microsoft Office的…

llama.cpp

https://github.com/echonoshy/cgft-llm 【大模型量化】- Llama.cpp轻量化模型部署及量化_哔哩哔哩_bilibili github.com/ggerganov/llama.cpp cd ~/code/llama.cpp/build_cuda/bin ./quantize --allow-requantize /root/autodl-tmp/models/Llama3-8B-Chinese-Chat-GGUF/Llama…

海尔智家:科技优秀是一种习惯

海尔智家&#xff1a;科技优秀是一种习惯 2024-06-28 15:19代锡海 6月24日&#xff0c;2023年度国家科学技术奖正式揭晓。海尔智家“温湿氧磁多维精准控制家用保鲜电器技术创新与产业化”项目荣获国家科学技术进步奖&#xff0c;成为家电行业唯一牵头获奖企业。 很多人说&…

分布式kettle调度管理平台简介

介绍 Kettle&#xff08;也称为Pentaho Data Integration&#xff09;是一款开源的ETL&#xff08;Extract, Transform, Load&#xff09;工具&#xff0c;由Pentaho&#xff08;现为Hitachi Vantara&#xff09;开发和维护。它提供了一套强大的数据集成和转换功能&#xff0c…

【C++】类、静态、枚举、重载、多态、继承、重写、虚函数

五、类 面向对象编程是一个巨大的编程范式。C中的类class就是基于对象的程序设计。 我们可以用类来定义一个新的类型&#xff0c;这些新类型就可以像内置类型一样使用。 内置类型颗粒度太太小&#xff0c;现实需求又非常复杂&#xff0c;这就需要我们把内置类型适度的进行拼搭…

源码学习:文件描述符

在进程描述学习中&#xff0c;扯到了max_fds&#xff0c;接着就联想到了日常运维中常见的ulimit参数、sysctl内核参数&#xff0c;原来以为max_fds与这些个关联性比较强&#xff0c;但经过一早上折腾以后&#xff0c;发现其实还是有一些差距的。但是在学习过程中&#xff0c;却…

【C++】数组、字符串

六、数组、字符串 讨论数组离不开指针&#xff0c;指针基本上就是数组的一切的基础&#xff0c;数组和指针的相关内容参考我的C系列博文&#xff1a;【C语言学习笔记】四、指针_通过变量名访问内存单元中的数据缺点-CSDN博客【C语言学习笔记】三、数组-CSDN博客 1、数组就是&…

数据结构03 链表的基本操作【C++数组模拟实现】

前言&#xff1a;本节内容主要了解链表的基本概念及特点&#xff0c;以及能够通过数组模拟学会链表的几种基本操作&#xff0c;下一节我们将通过STL模板完成链表操作&#xff0c;可以通过专栏进入查看下一节哦~ 目录 单链表及其特点 完整链表构成 完整链表简述 创建单链表 …

京东云备案流程图_云主机快速ICP备案_京东云服务器备案问题解答

京东云ICP备案流程&#xff0c;备案包括网站和APP备案&#xff0c;以及备案问题解答FAQ&#xff0c;阿腾云以京东云网站域名备案流程为例&#xff0c;先填写主办单位信息&#xff0c;选择网站备案或APP备案&#xff0c;申请授权码并验证&#xff0c;填写并上传主办单位详细信息…

光伏仿真软件是什么?都有哪些功能?

光伏仿真软件&#xff0c;作为现代光伏系统设计的重要工具&#xff0c;正日益受到设计师、工程师和决策者的青睐。它结合了物理学、工程学和计算机科学的原理&#xff0c;以数字化方式模拟光伏系统的运行&#xff0c;帮助用户预测和优化系统的性能。本文将详细探讨光伏仿真软件…

[Go 微服务] go-micro + consul 的使用

文章目录 1.go-micro 介绍2.go-micro 的主要功能3.go-micro 安装4.go-micro 的使用4.1 创建服务端4.2 配置服务端 consul4.3 生成客户端 5.goodsinfo 服务5.1 服务端开发5.2 客户端开发 1.go-micro 介绍 Go Micro是一个简化分布式开发 的微服务生态系统&#xff0c;该系统为开…

Java学习【IO流:深入理解与应用(上)】

Java学习【IO流&#xff1a;深入理解与应用&#xff08;上&#xff09;】 &#x1f343;1.IO流体系结构&#x1f343;2.FileOutputStream&#x1f341;2.1FileOutputStream写数据的三种方式&#x1f341;2.2换行和续写 &#x1f343;3.FileInputStream&#x1f341;3.1每次读取…