1.基本概念与操作
课本概念:程序的⼀个执⾏实例,正在执⾏的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。
当前:进程 = 内核数据结构(task_struct) + ⾃⼰的程序代码和数据
1.1 描述进程--PCB
进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block), Linux 操作系统下的 PCB 是: task_struct
task_struct-PCB的⼀种•
在 Linux 中描述进程的结构体叫做 task_struct 。
task_struct 是 Linux 内核的⼀种数据结构类型,它会被装载到RAM(内存)⾥并且包含着进
程的信息
1.2 task_struct
内容分类:
标⽰符:描述本进程的唯⼀标⽰符,⽤来区别其他进程。
状态:任务状态,退出代码,退出信号等
优先级:相对于其他进程的优先级。
程序计数器: 程序中即将被执⾏的下⼀条指令的地址。
内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下⽂数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]。
I∕O状态信息: 包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表。
记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。
组织进程:
可以在内核源代码⾥找到它。所有运⾏在系统⾥的进程都以task_struct 双链表的形式存在内核 ⾥。
1.3 查看进程信息
1. 进程的信息可以通过/proc系统⽂件夹查看
如:要获取PID为1的进程信息,你需要查看 /proc/1 这个⽂件夹。
1.4通过系统调⽤获取进程标⽰符
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { printf("pid: %d\n", getpid()); printf("ppid: %d\n", getppid()); return 0; }1.5 通过系统调⽤创建进程-fork初识
如果fork()返回值== 0,则为子进程。
> 0,则为父进程。
< 0,则为错误
由于fork()值返回之前,子进程就已被创造,所以两个进程都会返回值
1.6进程状态
/* *The task state array is a strange "bitmap" of *reasons to sleep. Thus "running" is zero, and *you can test for combinations of others with *simple bit tests. */ static const char *const task_state_array[] = { "R (running)", /*0 */ "S (sleeping)", /*1 */ "D (disk sleep)", /*2 */ "T (stopped)", /*4 */ "t (tracing stop)", /*8 */ "X (dead)", /*16 */ "Z (zombie)", /*32 */ };1.7 Linux内核代码关于进程
⼀个进程可以有⼏个状 态(在Linux内核⾥,进程有时候也叫做任务)。下⾯的状态在kernel源代码⾥定义:
/* *The task state array is a strange "bitmap" of *reasons to sleep. Thus "running" is zero, and *you can test for combinations of others with *simple bit tests. */ static const char *const task_state_array[] = { "R (running)", /*0 */ "S (sleeping)", /*1 */ "D (disk sleep)", /*2 */ "T (stopped)", /*4 */ "t (tracing stop)", /*8 */ "X (dead)", /*16 */ "Z (zombie)", /*32 */ };1.8进程状态查看
ps aux / ps axj 命令1.9 补充概念-竞争、独⽴、并⾏、并发
2.Z(zombie)-僵⼫进程
•僵死状态(Zombies)是⼀个⽐较特殊的状态。当进程退出并且⽗进程(使⽤wait()系统调⽤,后 ⾯讲)没有读取到⼦进程退出的返回代码时就会产⽣僵死(⼫)进程
• 僵死进程会以终⽌状态保持在进程表中,并且会⼀直在等待⽗进程读取退出状态代码。
• 所以,只要⼦进程退出,⽗进程还在运⾏,但⽗进程没有读取⼦进程状态,⼦进程进⼊Z状态
危害
当内存不够时会将进程的data和code放置swap分区,此时为挂起状态!
如下:
3.孤儿进程
•⽗进程先退出,⼦进程就称之为“孤⼉进程”
• 孤⼉进程被1号init/systemd进程领养,当然要有init/systemd进程回收喽。
4.进程优先级
• cpu资源分配的先后顺序,就是指进程的优先权(priority)。
• 优先权⾼的进程有优先执⾏权利。配置进程优先权对多任务环境的linux很有⽤,可以改善系统性 能。
• 还可以把进程运⾏到指定的CPU上,这样⼀来,把不重要的进程安排到某个CPU,可以⼤ 改善 系统整体性能。
4.1查看系统进程
在linux或者unix系统中,⽤ps‒l命令则会类似输出以下⼏个内容:
4.2PRI and NI
4.3 PRI VS NI
5.进程切换
CPU上下⽂切换:其实际含义是任务切换,或者CPU寄存器切换。当多任务内核决定运⾏另外的任务 时,它保存正在运⾏任务的当前状态,也就是CPU寄存器中的全部内容。这些内容被保存在任务⾃⼰的堆栈中,⼊栈⼯作完成后就把下⼀个将要运⾏的任务的当前状况从该任务的栈中重新装⼊CPU寄存器,并开始下⼀个任务的运⾏,这⼀过程就是context switch。
简单来说,就是CPU 从正在运行的进程 A “暂停”下来,保存好 A 的现场,然后“恢复”并继续运行进程 B 的全过程。
时间⽚:当代计算机都是分时操作系统,没有进程都有它合适的时间⽚(其实就是⼀个计数 器)。时间⽚到达,进程就被操作系统从CPU中剥离下来。
6.Linux2.6内核进程O(1)调度队列
6.1 活动队列
6.2 过期队列
• 过期队列和活动队列结构⼀模⼀样
6.3 active指针和expired指针
6.4 总结
• 在系统当中查找⼀个最合适调度的进程的时间复杂度是⼀个常数,不随着进程增多⽽导致时间成 本增加,我们称之为进程调度O(1)算法!
7. 命令⾏参数和环境变量
7.1 基本概念
环境变量(environment variables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数
环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性。
7.2和环境变量相关的命令
7.3和环境变量相关的一些事宜
1.环境变量和C代码,进程的关系:
进程通过代码获得环境变量
2.谁传递的环境变量
in bash -> process -> memoy -> data
其中bash 来自linux配置文件,进程里所有的代码和指令来自bash的子进程。
memory为临时的
8.程序地址空间
8.1空间布局图
8.2虚拟地址
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int g_val = 0; int main() { pid_t id = fork(); if(id < 0){ perror("fork"); return 0; } else if(id == 0){ //child,⼦进程肯定先跑完,也就是⼦进程先修改,完成之后,⽗进程 再读取 g_val=100; printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val); }else{ //parent sleep(3); printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val); } sleep(1); return 0; }
输出结果:
//与环境相关,观察现象即可 child[3046]: 100 : 0x80497e8 parent[3045]: 0 : 0x80497e8我们发现,⽗⼦进程,输出地址是⼀致的,但是变量内容不⼀样!能得出如下结论:
OS必须负责将 虚拟地址 转化成 物理地址
8.3 进程地址空间
分⻚&虚拟地址空间
上⾯的图就⾜矣说明问题,同⼀个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映 射到了不同的物理地址!
8.5 虚拟内存管理
描述linux下进程的地址空间的所有的信息的结构体是mm_struct (内存描述符)。每个进程只有⼀ 个mm_struct结构,在每个进程的 task_struct 结构中,有⼀个指向该进程的mm_struct结构体指 针。
struct task_struct { /*...*/ struct mm_struct *mm; //对于普通的⽤⼾进程来说该字段指向他 的虚拟地址空间的⽤⼾空间部分,对于内核线程来说这部分为NULL。 struct mm_struct *active_mm; // 该字段是内核线程使⽤的。当 该进程是内核线程时,它的mm字段为NULL,表⽰没有内存地址空间,可也并不是真正的没有,这是因 为所有进程关于内核的映射都是⼀样的,内核线程可以使⽤任意进程的地址空间。 /*...*/ }可以说, mm_struct 结构是对整个⽤⼾空间的描述。每⼀个进程都会有⾃⼰独⽴的 mm_struct , 这样每⼀个进程都会有⾃⼰独⽴的地址空间才能互不⼲扰。先来看看由 task_struct 到 mm_struct ,进程的地址空间的分布情况:
那既然每⼀个进程都会有⾃⼰独⽴的 mm_struct ,操作系统肯定是要将这么多进程的 mm_struct 组织起来的!虚拟空间的组织⽅式有两种:
1. 当虚拟区较少时采取单链表,由mmap指针指向这个链表;
2. 当虚拟区间多时采取红⿊树进⾏管理,由mm_rb指向这棵树。
linux内核使⽤ vm_area_struct 结构来表⽰⼀个独⽴的虚拟内存区域(VMA),由于每个不同质的虚 拟内存区域功能和内部机制都不同,因此⼀个进程使⽤多个vm_area_struct结构来分别表⽰不同类型 的虚拟内存区域。上⾯提到的两种组织⽅式使⽤的就是vm_area_struct结构来连接各个VMA,⽅便进 程快速访问。
8.6 为什么要有虚拟地址空间
且虚拟地址和物理地址解耦,进程管理和内存管理解耦