详解进程 及 探查进程
Posted 奇妙-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解进程 及 探查进程相关的知识,希望对你有一定的参考价值。
进程的概念
简而言之,进程就是正在在执行的程序
之前说过,程序执行的第一步Windows是双击程序Linux是 ./ ,系统接收到该命令时就会将磁盘中的程序代码载入到内存中,并为程序分配空间。
当程序被加载到内存后建立了自己的PCB,那他就是进程了。
PCB是什么
PCB的概念:含有该进程各种 信息 及 属性 的 数据结构,比如进程 ID ,用户 ID ,组 ID ,进程状态 ,优先级 ,I /O状态信息及程序数据等。
PCB是一般课本上的称呼(process control block), Linux操作系统下被称为PCB的实体是: task_struct
task_struct的作用
task_struct 是 Linux内核的一种数据结构,它会被装载到 RAM(内存) 里并且包含着进程的信息
其包含内容大致有:
1. 标示符: 描述本进程的唯一标示符,用来区别其他进程。
2. 状态: 任务状态,退出代码,退出信号等。
3. 优先级: 相对于其他进程的优先级。
4. 程序计数器: 程序中即将被执行的下一条指令的地址。
5. 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块
的指针
6. 上下文数据: 进程执行时处理器的寄存器中的数据,可以理解为存储的是程序
已经做了什么,下一步该做那些
8. I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用
的文件列表。
9. 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号
等。
其他信息等
如何执行进程
首先咱要明白,Linux 下你所有的 命令 与你能 执行的操作 都与权限有关。
触发任何一个事件,系统都会将其定义成一个进程,并给予这个进程一个专属 ID ,称为PID ,同时根据触发这个进程的用户与相关属性关系,给予该进程PID设置对应权限
运行程序也一样也要受到权限的约束,所以task_struct 要存有必要的属性信息
正常情况下,task_struct会在内存中像链表一样依次排列,逐个进行
,但我们要知道任何事情都要分个轻重缓急,操作系统在执行进程时更是如此。
所以进程对应的task_struct中便有了优先级这一属性,他用PRI值来表示优先级,PRI值越小则优先级越高,所以下图中PRI为60的会比PRI为80的优先得到CPU的资源
优先级:是CPU分配资源的先后顺序
上图中PRI值只有两种是为了大家方便理解,实际上PRI值是根据进程属性决定的(NI值也会影响进程优先级后边会补充)
优先级的必要性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
进程的探查
说了那么多,是时候见见进程了
在Linux下可用 ps 命令查看进程
命令:ps
作用: 查看属于当前用户的进程
上图可以看到输入 ps 回车后只看到了当前 ps 命令的进程及 bash 的进程,由此可以得出结论:命令只是某一程序快捷方式,执行时也会创建进程
什么是bash
每个用户登录时都会创建一个 bash 进程,各用户之间的 bash 进程互相之间没有影响。
1. bash是一个命令处理器, 运行在文本窗口中, 并能执行用户直接输入的命令.
2. bash还能从文件中读取Linux命令, 称之为脚本.
3. bash支持通配符, 管道, 命令替换, 条件判断等逻辑控制语句
总结: bash可以称为命令行解释器,我么在命令行执行的进程都由他衍生而来。
ps命令的使用(查看进程)
首先先了解进程信息列表每一项分别表示什么
F :内核分配给进程的系统标志
UID : 启动该进程的用户
PID : 进程ID
PPID : 父进程ID(如果该进程是其他进程启动的)
PRI : 进程优先级(值越小,优先级越大)
NI :优先级的修正值
VSZ : 进程占用虚拟内存空间的大小
RSS :进程在未被交换出时占用的物理内存大小
WCHAN :进程休眠的内核函数地址
STST : 代表当前进程状态(R是可运行正在等待;O是正在运行;S代表休眠;Z代表僵化,已终止但找不到其父进程 ;T代表停止)
上面简单演示了ps命令,但其只显示了两行极短的内容这显然不是咱想要的
下面演示ps的BSD风格的指令
指令: ps la
作用:采用长格式显示与任意终端关联的所有进程
可以看到显示了好几个进程,其中有两个bash进程,因为这台现在有两个账号在登陆这。
其中右边一栏 COMMAND 是程序名,图中的./my_exe程序只是目前在另一个账号上运行的程序,其PID是19795,PPID表示父进程./my_exe程序
PPID为18505,而18505恰好是bash的PID.
所以得出第二个结论:在Linux命令行执行的命令,产生的进程都由bash进程衍生而来,也就是说命令行上执行产生的进程其PPID(父进程)都为bash
下图为ps的命令选项,由于都是查询类选项我就不一一示范了
选项 | 描述 |
---|---|
T | 显示与当前终端关联的所有进程 |
a | 显示与任意终端关联的所有进程 |
g | 显示包括控制进程在内的所有进程 |
r | 仅显示运行中的进程 |
x | 显示所有进程,包括未分配任何终端的进程 |
U userlist | 显示属于userlist列表中某个用户ID所有的进程 |
p pidlist | 显示PID在pidlist列表中的进程 |
t ttylist | 显示与ttylist列表中的某个终端关联的进程 |
o format | 除了标准列,还输出由 format指定的列 |
x | 以寄存器格式显示数据 |
z | 在输出中包含安全信息 |
j | 显示作业信息 |
l | 采用长格式显示 |
o format | 仅显示由format指定的列 |
s | 采用信号格式显示 |
u | 采用基于用户的格式显示 |
v | 采用虚拟内存格式显示 |
N namelist | 定义要在 wCHAN输出列中显示的值 |
o order | 定义信息列的显示顺序 |
s | 将子进程的数值统计信息(比如CPU和内存使用情况)汇总到父进程中 |
c | 显示真实的命令名称(用以启动该进程的程序名称) |
e | 显示命令使用的环境变量 |
f | 用层级格式来显示进程,显示哪些进程启动了哪些进程 |
h | 不显示头信息 |
k sort | 指定用于排序输出的列 |
n | 使用数值显示用户ID、组ID以及 wCHAN信息 |
w | 为更宽的终端屏幕生成宽输出 |
H | 将线程显示为进程 |
m | 在进程之后显示线程 |
L | 列出所有的格式说明符 |
v | 显示ps命令的版本 |
创建进程
方法:进程可使用系统调用 fork() 函数创建新进程。
其中调用 fork() 的进程被称为父进程,新创建的进程被称为子进程
如下:在main中创建一个子进程
远行结果如下
如上所示使用 fork() 创建了子进程后,bbbbbb…居然被打印了两次,而aaaa…输出正常只打印了一次
为探究为什么咱使用两个函数辅助咱们观看结果
函数:getpid()
作用:返回当前进程PID
函数:getppid()
作用:返回当前进程PPID(父进程)
重新对代码编辑如下:
#include<unistd.h>
#include<stdio.h>
int main()
printf("aaaaaaaaaaaaaaaaaaa\\n");
fork(); //创建一个子进程
printf("我的PID :%d 我的PPID :%d \\n",getpid(),getppid());
return 0;
执行程序后结果如下
可以看出第一个打印出自己PID 6389 的为父进程 , 因为第二个打印出自己的PPID 为 6389,所以PID 6390为子进程
但也说明了父子进程执行的代码是一样的。
探究父子进程
查看 fork 的说明:
1:如果子进程开启成功则给父进程返回子进程的PID,否则返回-1
2:会给子进程返回0
由此我下下以下程序:
1 #include<unistd.h>
2 #include<stdio.h>
3 int main()
4
5 int n = 5;
6 int i = 1;
7 printf("aaaaaaaaaaaaaaaaaaa\\n");
8 int pp = fork(); //创建一个子进程
9 if(pp == 0)//子进程程序
10
11 while(n--)
12
13 printf("我是子进程,i = %d 我的PID :%d 我的PPID :%d \\n",i ,getpid(),getppid());
14 sleep(1); i =99;//把值改掉
15
16
17 else
18
19 while(n--)
20
21 printf("我是父进程,i = %d 我的PID :%d 我的PPID :%d \\n",i ,getpid(),getppid()); sleep(1);
22
23
24 return 0;
25
如上图 pp 变量会接收 fork() 的返回值(目前不考虑进程创建失败的情况),上面已经叙述了父进程与子进程的代码是一致的 ,只是父进程中 pp 变量接收到的 fork() 返回值为其子进程的PID ,而子进程中的 pp 变量接收到的 fork() 返回值为0
所以上述代码中,子进程和父进程所执行的代码一致(子进程从fork()函数位置开始执行),只是 pp 变量不同
大致可以如下图
程序执行结果如下
和我们预想的一致,由于父子进程中的 pp 变量不同所以被 if 和 else 区分打印出了对应的
但程序代码的第 14 行,在子进程中把 i 的值改成了99,但父进程的i值并没有被改变
总结:内核通过对父进程的复制来创建子进程,子进程从父进程处继承数据段,栈段,以及堆段的副本后,可以修改这些内容,不会影响父进程的内容(在内存中被标记为只读的程序文本段则由父,子进程共享);
Linux进程状态详解及状态转换
学而不思则罔,思而不学则殆。
Linux下,进程状态有五种 : 运行态,可中断睡眠态,不可中断睡眠态,停止态和追踪态
运行态表示进程可执行或者正在执行,
可中断睡眠态表示进程被阻塞,等条件满足就会被唤醒,也可以提前被信号打断唤醒。
不可中断睡眠态和可中断睡眠态是一样的,除了他不会被信号打断。
停止态就是进程暂停运行,类似挂起,不能被信号打断。
追踪态表示被打断点跟踪的进程。
死亡态表示进程已经终止并被回收。
以上是关于详解进程 及 探查进程的主要内容,如果未能解决你的问题,请参考以下文章