1、冯诺依曼体系结构
- 输入设备:键盘,鼠标,网卡,磁盘等。
Linux 进程的核心概念,包括冯诺依曼体系结构、操作系统定义及目的。详细阐述了进程控制块(PCB)、struct task_struct 结构体及其作用。讲解了进程的基本操作如查看进程、fork 创建子进程,以及进程状态(运行、阻塞、僵尸等)。深入分析了进程优先级(PRI、NI)、竞争与并发概念、上下文切换机制和 O(1) 调度队列。此外还涵盖了环境变量概念、命令及特点,最后探讨了进程虚拟地址空间与分页机制的作用。

注意:
操作系统包括:
操作系统,是一款进行 软硬件管理 的软件。管理:先描述 (类),再组织 (数据结构)。
system call (系统调用),驱动程序,都是为了屏蔽底层细节,外部实现统一。安全且方便。
PCB(Process Control Block),进程控制块,一种类型,Linux中的 PCB 为:struct task_struct。
内容分类(后续会详细介绍)
在 Linux 内核中,所有进程均通过 struct task_struct 结构体描述,并以双向链表的形式 (即队列) 组织和管理。
进程 = 内核数据结构对象 (PCB)+ 代码和数据
对进程的管理,就是对数据结构的增删改查。
ls /proc/1/ # 查看 PID=1 的进程信息(通常是 init/systemd)
top # 默认动态显示所有进程(按 CPU 占用排序)
top -p PID1,PID2,PID3 # 只监控指定 PID 的进程
top -u username # 只显示某用户的进程
交互命令(在 top 运行时使用)
ps aux # 适用于查看所有进程的资源占用,进程状态等
ps -l PID # 适用于查看进程的父子关系,进程优先级等
注意:
如:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("PID: %d\n", getpid()); // 当前进程 ID
printf("PPID: %d\n", getppid()); // 父进程 ID
return 0;
}
通过 fork(系统调用),创建子进程。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int ret = fork();
printf("hello proc : %d!, ret: %d\n", getpid(), ret);
sleep(1);
return 0;
}
以上可以分为三类:
注意:
一个 CPU,一个调度队列
PCB 对象,可以同时在不同的数据结构中,即可以在不同的队列中。
进程的状态,就是PCB 对象在不同队列之间的流动,本质是数据结构的增删改查。
阻塞是进程的 正常状态(因等待资源主动暂停),而 饥饿是 异常现象(可能是一直阻塞,或进程可能无需等待资源,但因调度问题无法运行等)
/*
* The task state array is a "bitmap" of reasons to sleep.
* "Running" is 0, other states can be combined via bit tests.
*/
static const char *const task_state_array[] = {
"R (running)", /* 0 - 运行中或就绪 */
"S (sleeping)", /* 1 - 可中断睡眠(等待事件)*/
"D (disk sleep)", /* 2 - 不可中断睡眠(通常等待 I/O)*/
"T (stopped)", /* 4 - 被信号暂停(如 SIGSTOP)*/
"t (tracing stop)", /* 8 - 被调试器跟踪暂停 */
"X (dead)", /* 16 - 完全终止(不会出现在任务列表)*/
"Z (zombie)", /* 32 - 僵尸进程(已终止但未回收)*/
};
模拟僵尸进程:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t id = fork(); // 创建子进程
if (id == 0) {
printf("child pid:>%d, ppid:>%d\n", getpid(), getppid());
return 0;
} else {
printf("parent pid:>%d, ppid:>%d\n", getpid(), getppid());
sleep(10);
}
return 0;
}
Z+,+表示在前台运行。
模拟孤儿进程:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t id = fork(); // 创建子进程
if (id == 0) {
printf("child pid:>%d, ppid:>%d\n", getpid(), getppid());
sleep(10);
return 0;
} else {
printf("parent pid:>%d, ppid:>%d\n", getpid(), getppid());
}
return 0;
}
进程得到 CPU 资源的先后顺序。
注意:
注意:
CPU 上下文切换(Context Switch),实际上是任务切换,或CPU 寄存器的切换。
流程:
注意:
环境变量是操作系统中用于指定运行环境参数的键值对 (KEY=VALUE)。
KEY是环境变量的名字,VALUE是环境变量的内容。
命令行:
系统调用:
int main(int argc,char* argv[],char* env[]),argv是命令行输入的命令字符串数组(以空格为分隔符,将命令分成若干个字符串,数组以 NULL 结尾),argc是argv 数组元素的个数,env是该进程 环境变量的字符串数组(环境变量放在字符串里,数组以 NULL 结尾)。注意:
以上关闭终端,重新登录,就会失效。想要永久生效,就要更改配置文件 (/.bashrc 或/.bash_profile),因为 bash 每次都是拷贝配置文件的内容。
以 32 位机器为例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int g_val = 0; // 全局变量,初始化为 0
int main() {
pid_t id = fork(); // 创建子进程
if (id < 0) { // fork 失败
perror("fork");
return 0;
} else if (id == 0) { // 子进程分支
g_val = 100; // 子进程修改全局变量
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
} else { // 父进程分支
sleep(3); // 父进程休眠 3 秒,确保子进程先执行
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
return 0;
}
为什么地址一样,内容却不一样?
说明:
所以,程序地址空间,准确来说是,进程虚拟地址空间。
首先,一个进程,一个虚拟地址空间。
struct task_struct {
/*...*/
struct mm_struct *mm; // 指向进程用户空间虚拟地址空间描述符
// - 对普通用户进程:指向其虚拟地址空间的用户空间部分
// - 对内核线程:NULL(因内核线程无独立用户空间)
struct mm_struct *active_mm; // 内核线程使用的替代 mm 字段
// - 内核线程的 mm 为 NULL 时,可借用其他进程的地址空间
// - 所有进程的内核空间映射相同,故内核线程可复用
/*...*/
};
// 1.当虚拟区较少时采取单链表,由 mmap 指针指向这个链表;
// 2.当虚拟区间多时采取红⿊树进⾏管理,由 mm_rb 指向这棵树。
struct mm_struct {
/*...*/
struct vm_area_struct *mmap; // 虚拟内存区域 (VMA) 链表头
struct rb_root mm_rb; // VMA 红黑树根节点(加速查找)
unsigned long task_size; // 用户虚拟地址空间大小
/* 各段地址边界 */
unsigned long start_code, end_code; // 代码段起止
unsigned long start_data, end_data; // 数据段起止
unsigned long start_brk, brk; // 堆段起止
unsigned long start_stack; // 栈起始地址
unsigned long arg_start, arg_end; // 命令行参数段
unsigned long env_start, env_end; // 环境变量段
/*...*/
};
struct vm_area_struct {
unsigned long vm_start; // 虚拟内存区域起始地址
unsigned long vm_end; // 虚拟内存区域结束地址
/* 链表与树结构 */
struct vm_area_struct *vm_next, *vm_prev; // 双向链表指针
struct rb_node vm_rb; // 红黑树节点
unsigned long rb_subtree_gap;
/* 关联的地址空间 */
struct mm_struct *vm_mm; // 所属的 mm_struct
/* 权限与标志 */
pgprot_t vm_page_prot; // 访问权限(读/写/执行)
unsigned long vm_flags; // 区域标志(如 VM_READ|VM_WRITE)
/* 共享与反向映射 */
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
/* 操作方法与文件映射 */
const struct vm_operations_struct *vm_ops; // 区域操作函数集
unsigned long vm_pgoff; // 文件映射偏移量(以页为单位)
struct file *vm_file; // 映射的文件指针(若为文件映射)
void *vm_private_data; // 驱动私有数据
/* 其他配置 */
atomic_long_t swap_readahead_info;
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; // NUMA 内存策略
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;
如图所示:
一个进程,一个页表,进行虚拟地址和物理地址的映射。
将物理地址转化为虚拟地址,提供给用户使用。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online