进程就是加载到内存中准备执行的程序。当进程创建的时候,内核赋予了唯一的标识号,这个标识号叫做进程ID或PID。

进程同一时间内可达数百个之多,为了管理这么多的进程。系统提供了一个调度器来维护。调度器维护一个所有进程的列表,每次选择一个进程(实际上可以一次选择多个进程),然后执行一个短暂的时间(时间片)。

典型的时间片是10毫秒。为了为了下一个时间片还可以顺利的执行,系统必须要保存下一条指令的位置,环境的副本等。

进程分叉到死亡

当进程需要使用内核的服务时,会使用系统调用发送请求。在编写程序时,系统调用的使用方法取决于语言。例如,c语言使用标准库中的函数进行调用。

下面列举一些常见的系统调用函数

系统调用 作用
进程控制
fork 创建当前进程的一个副本
wait 等待另一个程序结束后执行(强制暂停当前进程)
exec 在当前进程中执行一个新进程
exit 中止当前进程
kill 杀死子进程
文件I/O
open 打开一个用于读取或写入的文件
read 从文件中读取数据
write 向文件中写入了数据
close 关闭文件

fork打开的一个新进程叫做当前进程的子女,当前进程是新进程的双亲。

例如,shell打开内部命令时首先就要调用fork创建一个新进程,当子进程结束时会调用exit释放资源,被杀死的进程叫做僵进程。但是进程表中却仍然保存子进程的数据,因为父进程可能需要这些数据。。

当子进程创建时,父进程停止运行,等到子进程被杀死之后,父进程先看一眼进程表中子进程的数据,然后开始运行,与此同时,子进程从进程表中被销毁。

孤儿进程,父进程,子进程

孤儿进程指的是父进程意外死亡,这时只有子进程。所以当子进程结束时,因为没有人来接受它,会一直留在进程表中,直到系统结束才会死亡。

当然,现代系统没有这么傻。孤儿进程会自动被init进程收养,通过这种方式,每当产生孤儿进程时,都会迅速的被init进程销毁。

除了父进程意外死亡外子进程也可能一直不死,这种情况一般是程序出现bug无法正常退出时产生的,这个程序会一直消耗系统资源。kill可以解决这种问题。kill会杀死父进程然后让init进程托管,在适当的时候会杀死子进程(我估计是一定时间,猜测)。

关于父进程,fork创建一个和父进程一模一样的副本,那么这时如何知道谁是父进程,谁是子进程呢?其实fork创建子进程完成之后会返回给父进程和子进程一个值,子进程返回值是零,父进程返回值大于零。如果某个进程得到了零,那么他就会开始工作,得到大于零的就会停止工作(通过wait使自己暂停)

init 第一个进程

假设进程是通过分叉创建的,那么除第一个进程外的进程一定会有父进程,也一定会有第一个进程。

实际上也是这样,Linux在启动时会创建一个特殊的进程,PID是0,这个进程叫做空闲进程。

在进行了一些操作之后,0号进程开始分叉创建1号进程。之后0号进程就会死亡,然后由1号进程进行剩余的初始化步骤,例如进行多次分叉创建其他进程。因为 他要执行初始化步骤,所以叫初始化进程,也就是init进程。

前台和后台进程

前台进程就是当前需要我们交互的进程,后台进程就是自己跑的进程。

例如: sort < temp > temp2 &。后面的与符号就是让程序自己去后面跑,这时我们就可以直接使用shell了。

但是后台进程不能从标准输入中读取,可以输出到标准输出中,这就带来一个问题。如果这个程序需要输入但是你却把它划分成后台程序,那么他会一直等待输入。

sleep 创建延迟

语法: sleep interval [s | m | h | d ]

interval是时间间隔,后面是单位,默认是秒。

例如 sleep 5 表示中断5秒。

作业控制

作业就是每一条输入的指令,该作业有一个唯一的作业号来标识。作业控制就是对输入的指令进行调控。常见的作业控制命令如下表。

作业控制命令
jobs 显示作业列表
ps 显示进程列表
fg 将作业移至前台
bg 将作业移至后台
suspend 挂起当前shell
^Z 挂起当前前台作业
kill 终止作业
suspend 挂起shell,例如超级用户暂时返回普通用户

echo $$ 显示当前shell的PID

echo $! 显示上一条移至后台的PID

stty tostop 挂起试图向终端写数据的后台作业

set -o monitor 允许作业控制

set -o notify 当后台作业结束时立刻通报

在后台运行作业

为了在后台运行作业,需要在命令的最后加一个&符号。

每次在后台运行作业时,系统都会自动分配一个作业号,从1开始,依次向后增加。

每次后台作业完成时,都会向终端发送一个信息表示已经完成。

fg 将作业移至前台

挂起可能令人有些误解,其实就是暂停程序。可以用fg将挂起的作业恢复。

例如当你用vi编辑一个文件时,突然忘了某些东西想通过man查一下,可以先用ctrl+Z把vi挂起,然后查找,查找完了之后又用fg命令将vi唤醒。

如果有挂起的程序那么关机时系统会给你提示,你可以选择关机或者先把挂起的程序移至前台。

语法: fg %[job]

fg %%代表唤起当前进程。因为一般进程挂起后都是运行别的进程,运行完了才又唤醒进程,此时挂起的进程又成了最前面的进程。

后面的job是作业编号,可以通过jobs程序查看。

bg和fg大致相同。

ps程序

ps程序是用来显示进程状态的

语法: ps [-aefFly] [-p pid] [-u userid]

ps 显示与当前用户标识和终端相关的进程
ps -a 所有与用户标识进程与终端相关的非守护进程
ps -e 所有进程
ps -p pid 显示指定pid的进程
ps -u userid 显示指定userid的进程
ps -ef 显示所有用户的进程,完整输出
ps -t 显示所有守护进程

下面列举了ps输出时的标题和所代表的含义

标题 含义
ADDR 进程表中虚拟地址
C 处理器利用率
CMD 命令名称
F 进程相关的标志
NI nice值,用于设置优先级
PID 进程ID
PPID 父进程ID
PRI 优先级(大数字 = 小优先级 )
RSS 内存预留空间大小
S 状态代码(D,R,S,T,Z)
STIME 累计系统时间
SZ 物理页大小
TIME 累计cpu时间
TTY 控制终端完整名称
UID 用户标识
WCHAN 等待通道

状态代码

D 不可中断睡眠,等待事件结束
I 空闲,超过20s的睡眠
R 正在运行或者可运行(可运行是在运行队列中等待)
S 可中断睡眠,等待时间结束
T 挂起
Z 僵进程

-ly 显示状态代码

监视系统进程

语法: top [-d delay] [-n count] [p pid[,pid]…]

top的作用是动态显示进程信息。

-d 每隔多少秒刷新一次

-n 只在特定的时间进行刷新

-p 对某几个进程进行监视

显示进程树

语法: pstree [-aAcGnpu] [pid | userid]

作用:因为进程几乎都是通过分叉产生的,所以这个作用是显示进程之间的关系(即谁是父进程谁是子进程)。

具体的可以去看联机手册

kill 杀死进程,向进程发送信号

语法: kill [-signal] pid… | jobid…

signal 含义
1 中止,注销或终端失去连接时发送给进程
2 中断,按下^C时发送
9 杀死,立刻杀死,进程不能捕获
15 终止,请求终止,进程不能捕获
18 继续,恢复挂起的进程,由fg或bg发送
19 停止(挂起),按^Z发送

设置进程优先级 nice,renice

优先级决定了你能享有的系统资源。nice程序用于设定优先级。

语法: nice [-n adjustment] command

使用nice时要注意,只能对外部程序例如软件和自己写的程序设置优先级,系统内部命令不能设定优先级。其次,一般只对后台程序降低优先级,对前台程序降低优先级是自己找罪受。

不使用nice时优先级是0,使用nice默认的优先级是10,数字越大优先级越低,最大可以设到20,最小可以设到-20.当设负数的时候是提高优先级。

renice重新设置优先级。

语法: renice niceness -p processid

niceness是nice值,processid是进程ID。

niec程序是在程序开始运行时确定的,使用nice程序后command程序便开始运行。而renice是对已运行的程序重新设置优先级。

守护进程

很多程序并不是由用户运行的,由系统运行的程序就是守护程序。相当于windows中的服务程序。这些程序不受终端控制