进程模型5-0号进程

内核版本架构作者GitHubCSDN
Linux-3.0.1armv7-ALux1206

0号进程的作用

  在 Linux 中除了 init_task 0号进程,所有的线/进程都是通过 do_fork 函数复制父线/进程创建得到,因为 0号进程产生时没有任何进程可以参照,只能通过静态方式构造进程描述符 task_struct 和进程控制块 thread_info,然后在内核编译完成后相当于创建完成。

  0号进程在完成必要的内核早期初始化和创建出 1号进程(所有用户进程的父进程)和 2号进程(所有内核线程的父进程)之后会退化为当前 cpu上运行队列的 idle 空闲进程,永久运行在内核态,继续默默守护整个内核。需要特殊说明的是 0号进程执行 start_kernel 不是通过调度执行的,而是内核直接跳转到 start_kernel,最后在 rest_init 中开启调度。

0号进程的创建

  在 linux 3.0.1 中 0号进程描述符 init_task 还是与具体的架构相关的,对于arm 架构位于 ./arch/arm/kernel/init_task.c 使用宏 INIT_THREAD_INFOINIT_TASK 定义了全局的 init_task 进程描述符和 init_thread_union 进程控制块。

union thread_union init_thread_union __init_task_data ={ INIT_THREAD_INFO(init_task) };struct task_struct init_task = INIT_TASK(init_task);

0号进程的描述符

#define INIT_TASK(tsk)  \
{                                   \.state      = 0,                        		\.stack      = &init_thread_info,/*内核栈*/      \.usage      = ATOMIC_INIT(2),               \.flags      = PF_KTHREAD,                   \.prio       = MAX_PRIO-20,                  \.static_prio    = MAX_PRIO-20,                  \.normal_prio    = MAX_PRIO-20,                  \.policy     = SCHED_NORMAL,                 \.cpus_allowed   = CPU_MASK_ALL,                 \.mm     = NULL,                     \.active_mm  = &init_mm,                 \
...\.tasks      = LIST_HEAD_INIT(tsk.tasks),  	\
...\.real_parent    = &tsk,                     \.parent     = &tsk,                     \
...\.comm       = "swapper",                    \.thread     = INIT_THREAD,                  \.fs     = &init_fs,                 \.files      = &init_files,                  \.signal     = &init_signals,                \.sighand    = &init_sighand,                \.nsproxy    = &init_nsproxy,                \
...\.pids = {                           \[PIDTYPE_PID]  = INIT_PID_LINK(PIDTYPE_PID),        \[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID),       \[PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),        \},                              \.thread_group   = LIST_HEAD_INIT(tsk.thread_group),     \
...\
}

  INIT_TASK 宏初始了 init_task 的进程描述符,当内核编译完成生成镜像,这个 init_task 就已经在数据段中占有自己的一席之地,init_task也决定了系统之后所有进程、线程的基因, 由它完成内核早期初始化后, 最终演变为0号进程idle, 永久运行在内核态。

0号进程的内核栈

#define init_thread_info	(init_thread_union.thread_info)union thread_union {struct thread_info thread_info;					/* 进程控制块信息 */unsigned long stack[THREAD_SIZE/sizeof(long)]; 	/* 8K大小的内核栈空间 */
};

  在 INIT_TASK 中将内核堆栈指向了 init_thread_info,这也通过宏定义初始化的全局变量,类型为 union thread_union。这种结构在 arm 满栈递减的栈模式下,stack[0] 相当于是栈底,stack[THREAD_SIZE/sizeof(long)] 相当于是栈顶,而 thread_info 控制块则是从栈底位置开始放置的进程控制块。0号进程的 init_task ->stack 内核栈指针指向 init_thread_union.thread_info 就相当于指向了内核栈的栈底位置。

  除了0号进程其余线/进程的内核堆栈都是在 fork 时申请得到,只有0号进程是通过全局变量直接初始化形成,那么这个0号进程的内核栈是放置在什么位置呢?在 定义全局 init_thread_union 是发现有一个 __init_task_data 修饰符,具体的宏定义如下:

#define __init_task_data __attribute__((__section__(".data..init_task")))

  这表明整个内核堆栈时在编译时被放置在 .data..init_task 段,即 init_task 的堆栈指向 “.data…init_task” 这个位置,通过搜索在 ./include/asm-generic/vmlinux.lds.h 中发现如下宏定义:

#define INIT_TASK_DATA(align)                       \. = ALIGN(align);                       \*(.data..init_task)

  继续搜索 INIT_TASK_DATA 发现在./arch/arm/kernel/vmlinux.lds.S 中的 .data 段最开始的位置占用了 THREAD_SIZE 大小的空间,在 arm 32bits下 THREAD_SIZE = 8K,刚好与 union thread_union 下的栈大小相同,至此0号进程的内核栈被创建完成。

.data : AT(__data_loc) {_data = .;      /* address in memory */_sdata = .;/** first, the init task union, aligned* to an 8192 byte boundary.*/INIT_TASK_DATA(THREAD_SIZE)..._edata = .;}

0号进程的sp设置

  通过宏定义已经将 0号进程 inti_task 的描述符和所需要的内核堆栈创建出来了,但是要想运行起来还必须将当前模式下的 sp 指针(sp_svc)设置到内核堆栈和合适位置。在芯片上电后,一般需要先经过 uboot 或其他固件的执行,在执行完成后跳转到内核入口开始内核早期且重要的初始化,最后跳转到 start_kernel 入口开始在 C语言环境下初始化内核,而堆栈指针 sp 的初始化就是在跳转到 start_kernel 之前设置的。这一切都发生 head.Shead-common.S 中。

/** The following fragment of code is executed with the MMU on in MMU mode,* and uses absolute addresses; this is not position independent.**  r0  = cp#15 control register*  r1  = machine ID*  r2  = atags/dtb pointer*  r9  = processor ID*/__INIT
__mmap_switched:adr r3, __mmap_switched_data @将 __mmap_switched_data是开辟的一片地址,存放了一些变量,将地址赋值给r3ldmia   r3!, {r4, r5, r6, r7} @ 从__mmap_switched_data 将下的4 words 载入到r4~r7寄存器,r3地址会自动递增cmp r4, r5                    @ Copy data segment if needed 判断 __data_loc 与 _sdata是否相同,不相同时需要重定位拷贝
1:  cmpne   r5, r6ldrne   fp, [r4], #4strne   fp, [r5], #4bne 1bmov fp, #0                    @ Clear BSS (and zero fp) 设置 fp 为0,并初始化.bss段为0
1:  cmp r6, r7strcc   fp, [r6],#4bcc 1bARM(   ldmia   r3, {r4, r5, r6, r7, sp}) @从ARM模式下继续将 5个words 载入到r4~r7和sp寄存器, 这里设置了0号进程的内核堆栈位置THUMB( ldmia   r3, {r4, r5, r6, r7}    ) @THUMB模式下只能最多一次出栈到4个寄存器THUMB( ldr sp, [r3, #16]       )str r9, [r4]            @ Save processor ID  将寄存器r9的值(processor ID)存储到 processor_idstr r1, [r5]            @ Save machine type  将寄存器r1的值(machine ID)存储到__machine_arch_typestr r2, [r6]            @ Save atags pointer 将寄存器r2的值(atags/dtb pointer)存储到__atags_pointerbic r4, r0, #CR_A       @ Clear 'A' bitstmia   r7, {r0, r4}    @ Save control register values 将寄存器r0,r4的值(cp#15 control register)存储到cr_alignmentb   start_kernel        @ 跳转到start_kernel开始进行内核初始化
ENDPROC(__mmap_switched).align  2.type   __mmap_switched_data, %object
__mmap_switched_data:           @ 定义 __mmap_switched_data 数据结构.long   __data_loc          @ r4. 其中__data_loc,_sdata,__bss_start,_end是在编译时赋值的.long   _sdata              @ r5.long   __bss_start         @ r6.long   _end                @ r7.long   processor_id        @ r4 其中processor_id,__machine_arch_type,__atags_pointer,cr_alignment是等待被被赋值的.long   __machine_arch_type     @ r5.long   __atags_pointer         @ r6.long   cr_alignment            @ r7.long   init_thread_union + THREAD_START_SP @ sp 设置到 (&init_thread_union + THREAD_SIZE - 8)的栈顶位置,设置好SP,后面就可以运行了.size   __mmap_switched_data, . - __mmap_switched_data

  最终在 __mmap_switched 汇编函数中执行可能需要的数据段重定位和 .bss 段清除,并将在汇编环境下获取到的信息赋值给C环境中的 processor_id, __machine_arch_type, __atags_pointer, cr_alignment,最后将 sp 指针设置到 0号进程内核栈的 (&init_thread_union + THREAD_SIZE - 8) 位置,最后进入 start_kernel 中执行初始化和创建1, 2号进程。

注意:在内核在 start_kernel 完成初始化之前,内核会一直使用这个栈来进行工作。

0号进程的退化

  在 start_kernel 执行到 rest_init 函数中时所有的初始化已经全部完成,并创建了内核态和用户态各自的进程始祖,此时 0号进程作为初始模版的使命已经结束。此时,init_task 进程便会退化成主处理器的 idle 进程,继续发挥重要作用。而多喝处理器中其他核的 idle 进程则是在 kernel_init(1号进程)中,为每个从处理器的运行队列上通过 fork 创建出来的 pid 同样为 0。

start_kernel
└── rest_init└── init_idle_bootup_task└── init_idle_bootup_task
static noinline void __init_refok rest_init(void)
{
.../** The boot idle thread must execute schedule()* at least once to get things moving:*/init_idle_bootup_task(current);/* 让init_task进程隶属到idle调度类中,即选择idle的调度相关函数 */preempt_enable_no_resched();    /* 打开抢占,但是不进行调度 */schedule();                     /* 执行进程切换,因为0号进程已经切换为idle进程了,所以主动调度,使1号和2号线程可以运行 */preempt_disable();              /* 重新关闭抢占 *//* Call into cpu_idle with preempt disabled,空闲进程进入while中 */cpu_idle();
}void __cpuinit init_idle_bootup_task(struct task_struct *idle)
{idle->sched_class = &idle_sched_class;
}

🌀路西法 的个人博客拥有更多美文等你来读。

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

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

相关文章

计算机二级考前急救(Word篇)

重点题(20套,标黄为精选10套):4,15,17,19,21,24,25,27,36,40,12,18,20,22&…

constant(safe-area-inset-bottom)和env(safe-area-inset-bottom)在uniapp中的使用方法解析

在微信小程序中,padding-bottom: constant(safe-area-inset-bottom); 和 padding-bottom: env(safe-area-inset-bottom); 这两个 CSS 属性用于处理 iPhone X 及更高版本设备的安全区域(safe area)。这些设备的底部有一个“Home Indicator”&a…

十二、Cluster集群

目录 一、集群简介1、现状问题2、集群作用 二、集群结构设计1、集群存储设2、消息通信设计 三、Cluster集群三主三从结构搭建1、redis.conf配置文件可配置项2、配置集群3、链接集群4、命令客户端连接集群并使用 四、集群扩容1、添加节点2、槽位分配3、添加从节点 五、集群缩容1…

Java基础 3.29

1.数组的相关注意事项 错误示范一 String strs[] new String[2]{"a", "b"}; 正确示范一 String strs[] new String[]{"a", "b"}; 让JVM自己判断有几个数据,无需再其中写明有几组数据 错误示范二 String strs[] new…

从入门到精通:HTML 项目实战中的学习进度(一)

一、基础夯实阶段 1.1 HTML 文档结构与核心语法 在 HTML5 的世界里&#xff0c;构建一个文档就像是搭建一座大厦&#xff0c;坚实的基础至关重要。HTML5 文档的基础框架以<!DOCTYPE html>声明开场&#xff0c;这就好比是给浏览器下达的一份 “指令书”&#xff0c;明确…

FFmpeg —— 实时绘制音频波形图(附源码)

🔔 FFmpeg 相关音视频技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 实时绘制音频波形图 步骤                 FFmpeg打开媒体文件,读取每一包数据,将音频数据包进行缓冲,一包一包处理音频缓冲,对音频缓冲包进行解码,读…

大数据学习(88)-zookeeper实现的高可用(HA)

&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一…

基于MFC按钮逻辑

void CSUCCES1Dlg::SetDlgItemState()//IDC_BTN_INIT初始化按钮 { GigeState state = GigeState::ARV_NONE;//GigeState是一个枚举类型,stat状态为ARV_NONE int idx = ((CListBox*)GetDlgItem(IDC_LIST_GIGE))->GetCurSel();//GetDlgItem 是 MFC 框架提供的一个成员…

EF Core 乐观并发控制(并发令牌)

文章目录 前言一、乐观并发的核心思想二、实现方法1&#xff09;使用并发令牌&#xff08;Concurrency Token&#xff09;2&#xff09;处理并发冲突 三、工作原理四、适用场景五、与悲观并发的对比六、最佳实践总结 前言 Entity Framework (EF) Core 默认支持 乐观并发控制&a…

解决 FFmpeg 使用 C/C++ 接口时,解码没有 shell 快的问题(使用多线程)

一、问题 硬件设备为香橙派 5Plus&#xff0c;最近需要使用硬件视频解码来加速 YOLO 的检测&#xff0c;shell 窗口的FFmpeg已经调通&#xff0c;详见文章&#xff1a; 编译支持 RKmpp 和 RGA 的 ffmpeg 源码_rk3588 ffmpeg mpp-CSDN博客https://blog.csdn.net/plmm__/article…

工业控制网络中常用的通信协议

1. 现场总线协议 Modbus 概述&#xff1a;Modbus 是最广泛使用的工业协议之一&#xff0c;主要用于串行通信&#xff0c;支持主/从架构&#xff0c;通过 RS-232 或 RS-485 传输&#xff0c;也有基于以太网的 Modbus TCP 版本。特点&#xff1a;简单易用&#xff0c;易于实现&am…

【Mac】npm error Error: EACCES: permission denied, mkdir‘/Users/...

问题描述&#xff1a;Mac电脑中的 vscode 下载依赖的时候提示没有权限&#xff1a; 故障分析 首先账号是有权限的&#xff0c;电脑就建了一个账号是管理员&#xff1b;在桌面用shell直接执行命令npm init 命令可以执行成功&#xff0c;那么问题就出在vscodes上面了&#xff0…

Ruby 简介

Ruby 简介 引言 Ruby 是一种广泛使用的动态、开源的编程语言,自 1995 年由日本程序员 Yukihiro Matsumoto(通称 Matz)设计以来,它以其优雅的语法、强大的库支持和跨平台特性赢得了全球开发者的青睐。本文将详细介绍 Ruby 的起源、特点、应用领域以及它在现代软件开发中的…

[Qt5] QMetaObject::invokeMethod使用

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

Windows10清理机器大全集

Windows10清理机器大全集 写在前面先这么个标题&#xff0c;逐渐补充禁止Update移除Microsoft Compatibility Telemetrywindows-defender-remover其它 写在前面 看到标题&#xff0c;读者已经就吐了。 我是说&#xff0c;我非常认可: IT从业者&#xff0c;如果你银子比较充足&…

【AI】NLP

不定期更新&#xff0c;建议关注收藏点赞。 目录 transformer大语言模型Google Gemma疫情网民情绪识别 整体框架 baseline构建 模型调参、模型优化、其他模型 数据trick、指标优化、magic feature 数据增强、伪标签、迁移学习 模型融合sklearn中TFIDF参数详解 频率阈值可以去掉…

如何为 Debian 和 Kali 系统更换软件源并更新系统

在 Linux 系统中&#xff0c;软件源&#xff08;Software Repository&#xff09;是获取软件包和更新的核心途径。然而&#xff0c;默认的软件源可能会因为地理位置、网络状况等原因导致下载速度缓慢&#xff0c;甚至无法访问。为了提升系统的软件获取效率&#xff0c;许多用户…

android 一步完成 aab 安装到手机

家人们谁懂&#xff01;在 Android 系统安装 aab 应用超麻烦。满心期待快速体验&#xff0c;却发现 aab 无法直装&#xff0c;得先转为 apks 格式&#xff0c;这过程复杂易错。好不容易转好&#xff0c;还得安装 apks&#xff0c;一番折腾&#xff0c;时间与耐心全耗尽。别愁&a…

mac部署CAT监控服务

在 Mac 上部署美团点评开源的 CAT 监控服务端&#xff0c;可以按照以下步骤操作&#xff1a; 1. 环境准备 1.1 安装依赖 确保已安装以下工具&#xff1a; JDK 8&#xff08;建议 OpenJDK 11&#xff09; MySQL 5.7&#xff08;存储监控数据&#xff09;&#xff08;8.0不支持…

C语言基础:第10天笔记

内容提要 函数 函数的概述 函数的分类 函数的定义 形参和实参 函数的返回值 函数 函数的概述 函数&#xff1a;实现一定功能的&#xff0c;独立的代码模块&#xff0c;函数是c程序的核心构成模块&#xff0c;可以说c程序就是由众多的函数组成&#xff0c;对于函数的使用…