当前位置: 首页 > news >正文

内核进程基础

进程定义

操作系统作为硬件的使用层,提供使用硬件资源的能力;进程作为操作系统的使用层,提供使用操作系统抽象出的资源层的能力。

进程:是指计算机中已运行的程序。进程本身不是基本的运行单位,而是线程的容器。程序是指令、数据及其组织形式的描述,进程才是程序(指令和数据)的真正运行实例。

Linux内核把进程叫做task(任务),进程的虚拟地址空间可分为用户虚拟地址空间内核虚拟地址空间,所有进程共享内核虚拟地址空间(内核页表全局唯一),每个进程有独立的用户虚拟地址空间。

进程有两种特殊的形式:没有用户虚拟地址空间的进程叫内核线程;共享用户虚拟地址空间的进程叫用户线程。共享同一个用户虚拟地址空间的所有用户线程叫线程组。


linux arm64架构进程虚拟地址空间布局:

- <font style="color:rgb(64, 64, 64);">用户空间,低地址(地址范围 </font>`**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0x0000_0000_0000_0000 - 0x0000_FFFF_FFFF_FFFF</font>**`<font style="color:rgb(64, 64, 64);">)。</font>
- <font style="color:rgb(64, 64, 64);">内核空间,高地址(地址范围 </font>`**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0xFFFF_0000_0000_0000 - 0xFFFF_FFFF_FFFF_FFFF</font>**`<font style="color:rgb(64, 64, 64);">)。</font>

Linux进程四要素

  • 有一段程序供其执行;
  • 有进程专用的系统堆栈空间;
  • 在内核有**task_struct**数据结构(进程描述符);
  • 有独立的存储空间,拥有专有的用户空间;

如果有前三条而缺少第四条,则称为线程;如果完全没有用户空间,则称为内核线程;如果共享用户空间则称为用户线程

进程描述符task_struct

linux中每个进程都应该一个进程描述符task_struct

内核为每个进程分配一个task_struct结构体;实际分配两个连续物理页面(8192字节);

struct task_struct {volatile long			state; // 进程的状态标志void				*stack; // 指向内核栈,每个进程都有一个内核栈/*下面这4个是进程调度策略和优先级*/int				prio;int				static_prio;int				normal_prio;unsigned int			rt_priority;const struct sched_class	*sched_class; // 表示该进程所属的调度器类int				nr_cpus_allowed;const cpumask_t			*cpus_ptr; // 允许进程在哪些CPU上运行cpumask_t			cpus_mask;// 这两个指针指向内存描述符。进程:mm和active_mm指向同一个内存描述符。内核线程:mm是空指针,// 当内核线程运行时,active_mm指向从进程借用内存描述符struct mm_struct		*mm; // 指向内存描述符; mm设置为NULL,则为内核线程struct mm_struct		*active_mm;pid_t				pid; // 全局的进程号pid_t				tgid; // 全局的线程组标识符/* Real parent process: */struct task_struct __rcu	*real_parent; // 指向真实的父进程/* Recipient of SIGCHLD, wait4() reports: */struct task_struct __rcu	*parent; // 指向父进程;如果进程被另一个进程使用系统调用跟踪,那么父进程是跟踪进程;// 否则是real_parentstruct task_struct		*group_leader; // 指向线程组的组长struct hlist_node		pid_links[PIDTYPE_MAX]; // 进程号,进程组标识符和会话标识符/* Objective and real subjective task credentials (COW): */const struct cred __rcu		*real_cred; // 指向主体和真实课题证书/* Effective (overridable) subjective task credentials (COW): */const struct cred __rcu		*cred; // 指向有效客体证书char				comm[TASK_COMM_LEN]; // 进程名称// 下面这两个成员用于UNIX系统:信号量和共享内存
#ifdef CONFIG_SYSVIPCstruct sysv_sem			sysvsem;struct sysv_shm			sysvshm;
#endif...
}

进程创建方法

在Linux内核中,新进程是从一个已经存在的进程中复制出来的;内核使用静态数据结构构造出0号内核线程,0号内核线程分叉生成1号内核线程和2号内核线程(kthreadd线程)。1号内核线程完成初始化后装载用户程序,变成1号进程,其他进程都是1号进程或者它的子孙进程分叉生成的;其他内核线程是kthreadd线程分叉生成的。

linux提供了2个系统调用(forkclone),都可以用来创建新的进程。fork是最常用的方式。fork创建的新进程是原进程的副本,采用了写时复制技术。

fork在内核中将工作委托给_do_fork,其中主要的工作是copy_process

当前进程执行其它程序的方法

在Linux中,**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">fork()</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">exec()</font>** 是进程创建和程序执行的核心系统调用,通常结合使用以实现新进程的启动(如Shell运行命令)。

fork()复制当前进程(父进程),生成一个几乎完全相同的子进程,使用了写时复制(COW)

在子进程中通过<font style="color:rgb(64, 64, 64);">exec()</font>执行新程序。加载新程序到当前进程空间,替换原有代码、数据、堆栈等。

下面是一个通过<font style="color:rgb(64, 64, 64);">fork</font>+<font style="color:rgb(64, 64, 64);">exec</font>,实现在当前进程中执行<font style="color:rgb(64, 64, 64);">ls</font>命令的测试代码。

#include <stdio.h>      // printf, perror
#include <unistd.h>     // fork, execl
#include <sys/types.h>  // pid_t
#include <sys/wait.h>   // waitint main() {pid_t pid;// 创建子进程pid = fork();if (pid < 0) {// fork 失败perror("fork failed");return 1;}if (pid == 0) {// 子进程printf("Child process (PID: %d) is running `ls -l`...\n", getpid());// 使用 execl 执行 /bin/ls 程序,参数依次是:// 第一个参数:程序路径// 第二个参数:argv[0],约定俗成写程序名(可随意,但最好与实际命令一致)// 后续参数:命令行参数(可多个),以 NULL 结尾execl("/bin/ls", "ls", "-l", NULL);// 如果 exec 成功,上面那句就会替换当前进程,下面的不会执行perror("exec failed");  // exec 调用失败return 1;} else {// 父进程printf("Parent process (PID: %d), waiting for child (PID: %d)...\n", getpid(), pid);// 等待子进程结束int status;waitpid(pid, &status, 0);  // 也可以用 wait(&status)if (WIFEXITED(status)) {printf("Child exited with status %d\n", WEXITSTATUS(status));} else {printf("Child terminated abnormally\n");}}return 0;
}

参考资料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux内核深度解析,余华兵
  3. Linux设备驱动开发详解,宋宝华
  4. linux kernel 4.12
http://www.xdnf.cn/news/735067.html

相关文章:

  • 界面控件DevExpress WinForms中文教程:Banded Grid View - 如何固定Bands?
  • 《 PyTorch 2.3革新:torch.compile自动生成CUDA优化内核全解》
  • 鸿蒙OSUniApp页面切换动效实战:打造流畅精致的转场体验#三方框架 #Uniapp
  • Go语言结构体:数据组织的艺术
  • 网络犯罪分子利用虚假ChatGPT安装程序实施攻击
  • 【Go语言】Fyne GUI 库使用指南 (面向有经验开发者)
  • XUANYING炫影-移动版-智能轻云盒SY900Pro和SY910_RK3528芯片_免拆机通刷固件包
  • PHP中文网文章内容提取免费API接口教程
  • JavaScript中的命名导出(暴露)
  • yolov8添加注意力机制
  • 避免空值判断
  • Fluence (FLT) 2026愿景:RWA代币化加速布局AI算力市场
  • 一、Python 常用内置工具(函数、模块、特性)的汇总介绍和完整示例
  • Go 中 `json.NewEncoder/Decoder` 与 `json.Marshal/Unmarshal` 的区别与实践
  • C++学习-入门到精通【10】面向对象编程:多态性
  • LangChain表达式 (LCEL)
  • C语言实现对哈希表的操作:插入新键值对与删除哈希表中键值对
  • 哪些岗位最易被AI替代?
  • Docker设置代理
  • ros2工程在普通用户下正常编译但root下编译无法成功也不会自动停止
  • RAG混合检索:倒数秩融合RRF算法
  • 零硬件成本玩转嵌入式通信!嵌入式仿真实验教学平台解锁STM8S串口黑科技
  • 对COM组件的调用返回错误 HRESULT E_FAIL
  • Linux操作系统之进程(四):命令行参数与环境变量
  • 统计C盘各种扩展名文件大小总和及数量的PowerShell脚本
  • << C程序设计语言第2版 >> 练习 1-23 删除C语言程序中所有的注释语句
  • Python基于Django的校园打印预约系统(附源码,文档说明)
  • 天拓四方工业互联网平台赋能:地铁电力配电室综合监控与无人巡检,实现效益与影响的双重显著提升
  • URL编码次数差异分析:一次编码 vs 二次编码
  • 【动手学深度学习】2.4. 微积分