Linux进程的相关知识介绍( 二 )


如下 , 我们查询当前shell下的进程:
代码如下:
root@vamei:~# ps -o pid , ppid , cmd
PID PPID CMD
16935 3101 sudo -i
16939 16935 -bash
23774 16939 ps -o pid , ppid , cmd
我们可以看到 , 第二个进程bash是第一个进程sudo的子进程 , 而第三个进程ps是第二个进程的子进程 。
还可以用$pstree命令来显示整个进程树:
代码如下:
init─┬─NetworkManager─┬─dhclient
│ └─2*[{NetworkManager}]
├─accounts-daemon───{accounts-daemon}
├─acpid
├─apache2─┬─apache2
│ └─2*[apache2───26*[{apache2}]]
├─at-spi-bus-laun───2*[{at-spi-bus-laun}]
├─atd
├─avahi-daemon───avahi-daemon
├─bluetoothd
├─colord───2*[{colord}]
├─console-kit-dae───64*[{console-kit-dae}]
├─cron
├─cupsd───2*[dbus]
├─2*[dbus-daemon]
├─dbus-launch
├─dconf-service───2*[{dconf-service}]
├─dropbox───15*[{dropbox}]
├─firefox───27*[{firefox}]
├─gconfd-2
├─geoclue-master
├─6*[getty]
├─gnome-keyring-d───7*[{gnome-keyring-d}]
├─gnome-terminal─┬─bash
│ ├─bash───pstree
│ ├─gnome-pty-helpe
│ ├─sh───R───{R}
│ └─3*[{gnome-terminal}]
fork通常作为一个函数被调用 。这个函数会有两次返回 , 将子进程的PID返回给父进程 , 0返回给子进程 。实际上 , 子进程总可以查询自己的PPID来知道自己的父进程是谁 , 这样 , 一对父进程和子进程就可以随时查询对方 。
通常在调用fork函数之后 , 程序会设计一个if选择结构 。当PID等于0时 , 说明该进程为子进程 , 那么让它执行某些指令 , 比如说使用exec库函数(library function)读取另一个程序文件 , 并在当前的进程空间执行 (这实际上是我们使用fork的一大目的: 为某一程序创建进程);而当PID为一个正整数时 , 说明为父进程 , 则执行另外一些指令 。由此 , 就可以在子进程建立之后 , 让它执行与父进程不同的功能 。
子进程的终结(termination)
当子进程终结时 , 它会通知父进程 , 并清空自己所占据的内存 , 并在kernel里留下自己的退出信息(exit code , 如果顺利运行 , 为0;如果有错误或异常状况 , 为》0的整数) 。在这个信息里 , 会解释该进程为什么退出 。父进程在得知子进程终结时 , 有责任对该子进程使用wait系统调用 。这个wait函数能从kernel中取出子进程的退出信息 , 并清空该信息在kernel中所占据的空间 。但是 , 如果父进程早于子进程终结 , 子进程就会成为一个孤儿(orphand)进程 。孤儿进程会被过继给init进程 , init进程也就成了该进程的父进程 。init进程负责该子进程终结时调用wait函数 。
当然 , 一个糟糕的程序也完全可能造成子进程的退出信息滞留在kernel中的状况(父进程不对子进程调用wait函数) , 这样的情况下 , 子进程成为僵尸(zombie)进程 。当大量僵尸进程积累时 , 内存空间会被挤占 。
进程与线程(thread)
尽管在UNIX中 , 进程与线程是有联系但不同的两个东西 , 但在Linux中 , 线程只是一种特殊的进程 。多个线程之间可以共享内存空间和IO接口 。所以 , 进程是Linux程序的唯一的实现方式 。

推荐阅读