2. 进程的创建

通常在命令行键入某个程序文件名以后,一个进程就被创建了。例如:

范例:让程序在后台运行

$ sleep 100 &
[1] 9298

范例:查看进程 ID

pidof可以查看指定程序名的进程ID:

$ pidof sleep
9298

范例:查看进程的内存映像

$ cat /proc/9298/maps
08048000-0804b000 r-xp 00000000 08:01 977399     /bin/sleep
0804b000-0804c000 rw-p 00003000 08:01 977399     /bin/sleep
0804c000-0806d000 rw-p 0804c000 00:00 0          [heap]
b7c8b000-b7cca000 r--p 00000000 08:01 443354
...
bfbd8000-bfbed000 rw-p bfbd8000 00:00 0          [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]

程序被执行后,就被加载到内存中,成为了一个进程。上面显示了该进程的内存映像(虚拟内存),包括程序指令、数据,以及一些用于存放程序命令行参数、环境变量的栈空间,用于动态内存申请的堆空间都被分配好。

实际上,创建一个进程,也就是说让程序运行,还有其他的办法,比如,通过一些配置让系统启动时自动启动程序(具体参考 man init),或者是通过配置 crond (或者 at)让它定时启动程序。除此之外,还有一个方式,那就是编写 Shell 脚本,把程序写入一个脚本文件,当执行脚本文件时,文件中的程序将被执行而成为进程。这些方式的细节就不介绍,下面了解如何查看进程的属性。

需要补充一点的是:在命令行下执行程序,可以通过 ulimit 内置命令来设置进程可以利用的资源,比如进程可以打开的最大文件描述符个数,最大的栈空间,虚拟内存空间等。具体用法见 help ulimit

查看进程的属性和状态

可以通过 ps 命令查看进程相关属性和状态,这些信息包括进程所属用户,进程对应的程序,进程对 cpu 和内存的使用情况等信息。熟悉如何查看它们有助于进行相关的统计分析等操作。

范例:通过 ps 命令查看进程属性

查看系统当前所有进程的属性:$ ps -ef

查看命令中包含某字符的程序对应的进程,进程 ID 是 1 。 TTY 为?表示和终端没有关联:

$ ps -C init
  PID TTY          TIME CMD
    1 ?        00:00:01 init

选择某个特定用户启动的进程

$ ps -U falcon

按照指定格式输出指定内容,下面输出命令名和 cpu 使用率:

$ ps -e -o "%C %c"

打印 cpu 使用率最高的前 4 个程序:

$ ps -e -o "%C %c" | sort -u -k1 -r | head -5
 7.5 firefox-bin
 1.1 Xorg
 0.8 scim-panel-gtk
 0.2 scim-bridge

获取使用虚拟内存最大的 5 个进程

$ ps -e -o "%z %c" | sort -n -k1 -r | head -5
349588 firefox-bin
 96612 xfce4-terminal
 88840 xfdesktop
 76332 gedit
 58920 scim-panel-gtk

范例:通过 pstree 查看进程亲缘关系

系统所有进程之间都有“亲缘”关系,可以通过 pstree 查看这种关系:$ pstree 会打印系统进程调用树,可以非常清楚地看到当前系统中所有活动进程之间的调用关系。

范例:用top动态查看进程信息

$ top

该命令最大特点是可以动态地查看进程信息,当然,它还提供了一些其他的参数,比如 -S 可以按照累计执行时间的大小排序查看,也可以通过 -u 查看指定用户启动的进程等。

补充: top 命令支持交互式,比如它支持 u 命令显示用户的所有进程,支持通过 k 命令杀掉某个进程;如果使用 -n 1 选项可以启用批处理模式,具体用法为:

$ top -n 1 -b

范例:确保特定程序只有一个副本在运行

下面来讨论一个有趣的问题:如何让一个程序在同一时间只有一个在运行。这意味着当一个程序正在被执行时,它将不能再被启动。那该怎么做呢?

假如一份相同的程序被复制成了很多份,并且具有不同的文件名被放在不同的位置,这个将比较糟糕,所以考虑最简单的情况,那就是这份程序在整个系统上是唯一的,而且名字也是唯一的。这样的话,有哪些办法来回答上面的问题呢?

总的机理是:在程序开头检查自己有没有执行,如果执行了则停止否则继续执行后续代码。

策略则是多样的,由于前面的假设已经保证程序文件名和代码的唯一性,所以通过 ps 命令找出当前所有进程对应的程序名,逐个与自己的程序名比较,如果已经有,那么说明自己已经运行了。

ps -e -o "%c" | tr -d " " | grep -q ^init$   #查看当前程序是否执行
[ $? -eq 0 ] && exit   #如果在,那么退出, $?表示上一条指令是否执行成功

每次运行时先在指定位置检查是否存在一个保存自己进程 ID 的文件,如果不存在,那么继续执行,如果存在,那么查看该进程 ID 是否正在运行,如果在,那么退出,否则往该文件重新写入新的进程 ID,并继续。

pidfile=/tmp/$0".pid"
if [ -f $pidfile ]; then
       OLDPID=$(cat $pidfile)
    ps -e -o "%p" | tr -d " " | grep -q "^$OLDPID$"
    [ $? -eq 0 ] && exit
fi
echo $$ > $pidfile
#... 代码主体
#设置信号0的动作,当程序退出时触发该信号从而删除掉临时文件
trap "rm $pidfile"      0

更多实现策略自己尽情发挥吧!