进程是操作系统中正在执行的一个命令或程序。在 Linux 系统当中,每当触发任何一个事件时,系统都会将它定义成为一个进程,并且给予这个进程一个ID,称为PID,同时根据触发进程用户的权限给予这个PID一组有效的权限设置。在有些命令执行结束后进程会终结,也有一些命令启动后会常驻在系统中,被称为常驻内存进程。
查看进程
在linux中最常用的查看进程的命令为ps命令,比如ps -l命令能查看当前bash 下的相关进程全部信息。
ps命令的选项:
a:显示一个终端的所有进程,除会话引线外;
u:显示进程的归属用户及内存的使用情况;
x:显示没有控制终端的进程;
-l:长格式显示更加详细的信息;
-e:显示所有进程;
它的部分选项不能加入"-",比如命令"ps aux",其中"aux"是选项,但是前面不能带“-”。
[root@localhost ~]# ps aux
#查看系统中所有的进程
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 2872 1416 ? Ss Jun04 0:02 /sbin/init
root 2 0.0 0.0 0 0 ? S Jun04 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S Jun04 0:00 [migration/0]
root 4 0.0 0.0 0 0 ? S Jun04 0:00 [ksoftirqd/0]
ps aux的输出信息含义:
VSZ: 该进程占用虚拟内存的大小,单位为 KB。
RSS: 该进程占用实际物理内存的大小,单位为 KB。
TTY: 该进程是在哪个终端运行的。其中:
tty1 ~ tty7 代表本地控制台终端(可以通过 Alt+F1 ~ F7 快捷键切换不同的终端)
tty1~tty6 是本地的字符界面终端,tty7 是图形终端。
pts/0 ~ 255 代表虚拟终端,一般是远程连接的终端,第一个远程连接占用 pts/0,第二个远程连接占用 pts/1,依次增长。
STAT: 进程状态。常见的状态有以下几种:
-D:不可被唤醒的睡眠状态,通常用于 I/O 情况。
-R:该进程正在运行。
-S:该进程处于睡眠状态,可被唤醒。
-T:停止状态,可能是在后台暂停或进程处于除错状态。
-W:内存交互状态(从 2.6 内核开始无效)。
-X:死掉的进程(应该不会出现)。
-Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。
-<:高优先级(以下状态在 BSD 格式中出现)。
-N:低优先级。
-L:被锁入内存。
-s:包含子进程。
-l:多线程(小写 L)。
-+:位于后台。
COMMAND: 产生此进程的命令名。
ps-le的输出信息含义:
ps-le可以比ps aux看到更多的详细信息,但基本功能与ps aux一致。
PRI:进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行。
NI:进程的优先级,数值越小,该进程越早被执行。
ADDR:该进程在内存的哪个位置。
SZ:该进程占用多大内存。
top命令
top 命令可以动态地持续监听进程的运行状态,与此同时,该命令还提供了一个交互界面,用户可以根据需要,人性化地定制自己的输出,进而更清楚地了进程的运行状态。
top命令的选项:
· -d 秒数:指定 top 命令每隔几秒更新。默认是 3 秒。
· -b:使用批处理模式输出。一般和"-n"选项合用,用于把 top 命令重定向到文件中。
· -n 次数:指定 top 命令执行的次数。一般和"-"选项合用。
· -p 进程PID:仅查看指定 ID 的进程。
· -s:使 top 命令在安全模式中运行,避免在交互模式中出现错误。
· -u 用户名:只监听某个用户的进程。
在 top 命令的显示窗口中,还可以使用如下按键,进行一下交互操作:
· ? 或 h:显示交互模式的帮助。
· P:按照 CPU 的使用率排序,默认就是此选项。
· M:按照内存的使用率排序。
· N:按照 PID 排序。
· T:按照 CPU 的累积运算时间排序,也就是按照 TIME+ 项排序。
· k:按照 PID 给予某个进程一个信号。一般用于中止某个进程,信号 9 是强制中止的信号。
· r:按照 PID 给某个进程重设优先级(Nice)值。
· q:退出 top 命令。
top命令例子:
[root@localhost ~]# top
top - 12:26:46 up 1 day, 13:32, 2 users, load average: 0.00, 0.00, 0.00
Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.1%us, 0.1%sy, 0.0%ni, 99.7%id, 0.1%wa, 0.0%hi, 0.1%si, 0.0%st
Mem: 625344k total, 571504k used, 53840k free, 65800k buffers
Swap: 524280k total, 0k used, 524280k free, 409280k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19002 root 20 0 2656 1068 856 R 0.3 0.2 0:01.87 top
1 root 20 0 2872 1416 1200 S 0.0 0.2 0:02.55 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.03 kthreadd
3 root RT 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
4 root 20 0 0 0 0 S 0.0 0.0 0:00.15 ksoftirqd/0
5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
6 root RT 0 0 0 0 S 0.0 0.0 0:10.01 watchdog/0
7 root 20 0 0 0 0 S 0.0 0.0 0:05.01 events/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cgroup
其中第一行的load average: 0.00,0.00,0.00表示系统在之前 1 分钟、5 分钟、15 分钟的平均负载。如果 CPU 是单核的,则这个数值超过 1 就是高负载:如果 CPU 是四核的,则这个数值超过 4 就是高负载。
第二行 Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie
表示进程信息,正在运行的进程、睡眠的进程、正在停止的进程数等。
Cpu(s): 0.1%us, 0.1%sy, 0.0%ni, 99.7%id, 0.1%wa, 0.0%hi, 0.1%si, 0.0%st
第三行表示cpu信息:
- us表示用户模式占用的cpu百分比
- sy表示系统占用百分比
- ni表示改变过优先级的用户进程占用cpu百分比
- id表示空闲cpu占用的百分比
- wa表示等待输入/输出的进程占用cpu百分比
- hi表示硬中断请求服务占用cpu百分比
- si表示软中断请求服务占用百分比。
第四行 Mem: 625344k total, 571504k used, 53840k free, 65800k buffers
表示物理内存信息,total,used,free,buffer表示内容已经很明确。
第五行 Swap: 524280k total, 0k used, 524280k free, 409280k cached
表示虚拟内存使用信息。
top的第二部分输出与ps命令类似:
VIRT表示该进程占用虚拟内存大小
RES表示占用物理内存大小。
Linux进程的优先级
在 Linux 系统中,表示进程优先级的有两个参数:Priority 和 Nice。
前面提到了用ps-le命令和top命令都可以查看进程的优先级,PRI 代表 Priority,NI 代表 Nice。这两个值都表示优先级,数值越小代表该进程越优先被 CPU 处理。不过,PRI值是由内核动态调整的,用户不能直接修改。
我们只能通过修改 NI(Nice) 值来影响 PRI 值,间接地调整进程优先级。
PRI和NI 的关系如下:PRI (最终值) = PRI (原始值) + NI。
我们修改 NI 的值就可以改变进程的优先级即可。
NI 值越小,进程的PRI就会降低,该进程就越优先被CPU处理;
反之,NI值越大,进程的PRI值就会増加,该进程就越靠后被 CPU处理。
修改NI值时有几个注意事项:NI 范围是 -20~19。
普通用户调整NI值的范围是0~19,而且只能调整自己的进程。
普通用户只能调高NI值,而不能降低。如原本NI值为0,则只能调整为大于0。
只有root用户才能设定进程NI值为负值,而且可以调整任何用户的进程。
调整进程的优先级
可以使用nice命令和renice命令:
nice命令可以给要启动的进程赋予NI值,但是不能修改已运行进程的NI值。
nice命令格式如下:
[root@localhost ~] # nice [-n NI值] 命令
同nice命令相反,renice 命令可以在进程运行时修改其NI 值,从而调整优先级。
renice 命令格式如下:
[root@localhost ~] # renice [优先级] PID
父进程&子进程
当Linux启动的时候,init 是系统创建的第一个进程,这一进程会一直存在,直到我们关闭计算机。所有其他的进程都是由init进程衍生出来的。
当我们登录系统后,会取得一个bash shell,然后我们利用这个bash提供的接口去执行另一个命令,例如bash或者ps等。那些另外执行的命令也会被触发成为PID,那个后来执行的命令产生的PID就是“子进程”,而原本的bash环境下,就称为“父进程”了。老进程成为新进程的父进程(parent process),而相应的,新进程就是老的进程的子进程(child process)。
一个进程除了有一个PID之外,还会有一个PPID(parent PID)来存储的父进程PID。如果我们循着PPID不断向上追溯的话,总会发现其源头是 init 进程。
fork & exec
当计算机开机的时候,内核只建立了一个 init 进程。Linux kernel 并不提供直接建立新进程的系统调用。剩下的所有进程都是init进程通过fork机制建立的。
新的进程要通过老的进程复制自身得到,这就是fork。
fork是一个系统调用。进程存活于内存中。
每个进程都在内存中分配有属于自己的一片空间 (内存空间,包含栈、堆、全局静态区、文本常量区、程序代码区)。
当一个程序调用 fork 的时候,实际上就是将上面的内存空间,又复制出来一个,构成一个新的进程,并在内核中为该进程创建新的附加信息 (比如新的 PID,而PPID为原进程的PID)。
此后,两个进程分别地继续运行下去。新的进程和原有进程有相同的运行状态(相同的变量值,相同的指令…)。我们只能通过进程的附加信息来区分两者。
程序调用exec的时候,进程清空自身的内存空间,并根据新的程序文件重建程序代码、文本常量、全局静态、堆和栈(此时堆和栈大小都为 0),并开始运行。
参考资料:
1. C语言中文网
2. 鸟哥私房菜