本文共 2787 字,大约阅读时间需要 9 分钟。
程序: 程序是一个文本文件,是静态的
进程: 进程是程序运行起来的一个表现形式,是操作系统分配资源的最小单位,在操作系统内核中,为进程创建一个 task_struct 的结构体,称之为 PCB (Process Control Block, 进程控制块),它是进程属性的集合。
进程是程序运行起来的一种表现形式,是操作系统分配资源的最小单位。
进程是资源管理的最小单位,线程是程序执行的最小单位。Linux 下的线程本质是用进程实现操作系统通过 PCB 实现对程序运行调度的控制。
task_struct是 PCB 的一种,是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
task_struct中包含的进程信息有:进程标识符 (PID)、状态、优先级、程序计数器、上下文数据、内存指针、I/O 状态信息、记账信息以及其它信息。
(1) 进程标识符PID
描述本进程的位图标识符,在当前操作系统中不会重复
(2) 进程状态
运行状态: 正在CPU上面进行运算
就绪状态: 程序已经准备好了,在就绪队列中等待获取CPU资源
阻塞状态: 等待 I/O 就绪
并发执行: 多个进程使用同一 CPU,每个进程都独占 CPU 一小会儿,操作系统进行进程切换,让每一个进程都可以使用 CPU
并行执行: 多个进程,在同一时刻,每一个进程都独占一个 CPU 进行运算
在操作系统内核中,具体的进程状态有:
R:运行状态 S:可中断睡眠状态 D:磁盘睡眠状态,不可被打断 T:暂停状态 t:跟踪状态,gdb调试程序时进程就处于 t 状态 X:死亡状态 Z:僵尸状态
补充:
“+”: 代表前台进程
没有 “+”: 代表后台进程
fg : 将最后一次放在后台的进程放入前台来运行
(3) 程序计数器
保存进程即将要执行的下一条指令 ,为下一次恢复现场使用
(4) 上下文数据
保存上一次执行时寄存器当中的值
补充:
- 进程在执行的时候,进程会进行切换,切换是操作系统调度的。
- 在被切换出去的时候,该进程当中的程序计数器会保存程序要执行的下一条指令,上下文信息会保存寄存器当中的值。
- 再次切换回来的时候,通过程序计数器和上下文信息来恢复之前的场景,继续进行。
(5) 进程优先级PR
PR(new) = PR(old) + NICE ,其中 NICE 的范围在 [-20,19],一共40个级别。PR 值越大,优先级越低,反之则优先级就越高。top
可以查看系统资源消耗情况。
补充:
top 命令更改已存在进程的 NICE① top; ② 进入 top 后输入 “r”; ③ 输入进程PID; ④ 输入 NICE 值
(6) 内存指针
内存指针指向了程序地址空间
(7) I/O信息和记账信息
记录当前进程启动时间,记录当前进程占用 CPU 的时间。ll /proc/[PID]/fd
打开文件信息
总结:
操作系统内核是用 task_struct 结构体来描述进程,用双向链表来组织进程的
#includepid_t fork(void);
让已经启动的程序(父进程)调用 fork 函数,来创建子进程,子进程的 PCB (task_struct) 是拷贝父进程的。下面例子可以理解子进程是拷贝父进程的PCB。
#include#include int main(){ printf("---begin---"); fork(); printf("fork 函数创建子进程"); return 0;}
#includepid_t fork(void);
fork函数有返回值:
子进程创建失败,返回小于0的值
子进程创建成功,返回两次。父进程中返回大于0的值(子进程的PID),子进程中返回值等于0
#includeint main(){ pid_t ret = fork(); if(ret < 0) { //创建子进程失败 } else if(ret > 0) { //父进程 } else { //子进程 } return 0;}
通过返回值的不同,可以让父进程执行不同的代码
通过 ps -ef | grep [程序名称]
或者 ps aux | grep [程序名称]
查看父/子进程的PID,从而确定哪个是子进程,哪个是父进程。
一旦子进程创建成功,父/子进程是抢占式执行。
通过终端启动的前台进程的父进程是 bash(命令行解释器)。
还可以通过函数获取进程的PID和PPID
#include#include pid_t getpid(void); //谁调用获取谁的进程PID pid_t getppid(void); //谁调用获取谁的父进程PID
#include#include pid_t vfork(void)
vfork 函数也是用来创建子进程的函数,vfork 函数创建出来的子进程拷贝父进程的 PCB,并且子进程 PCB 当中的内存指针指向了父进程的进程虚拟地址空间,也就是说父子进程共用同一块进程虚拟地址空间。从下面这幅图也可以看出它们之间的区别。
但是,vfork 存在一个问题在于,父子进程指向同一块进程虚拟地址空间,如果父子进程同时并行运行,有可能导致函数调用栈混乱的问题,vfork 的解决方法是让子进程先运行,等子进程结束后,再让父进程运行。相关推荐文章:
总结:
1. 调用 fork 函数,fork 函数是一个系统调用
2. 父进程调用 fork 函数创建子进程,子进程拷贝父进程的 PCB (task_struct),父进程和子进程是独立的两个进程
3. 子进程从 fork 函数之后的代码开始运行,程序计数器(保存了程序即将要执行的下一条指令)+ 上下文信息(保存了寄存器当中的值)
4.子进程和父进程是代码共享,数据独有
转载地址:http://lnwxi.baihongyu.com/