进程标识符(PID)是一个进程的基本属性,其作用类似于每个人的身份证号码。根据进程标识符,用户可以精确地定位一个进程。一个进程标识符唯一对应一个进程,而多个进程标识符可以对应同一个程序。本文将深入探讨进程标识符及其相关操作。
1 进程标识符
每个进程在系统中都有唯一的一个ID标识它,这个ID就是进程标识符(PID)。因为其唯一,所以系统可以根据它准确定位到一个进程。进程标识符的类型为pid_t,其本质上是一个无符号整型的类型别名(typedef)。
接下来,我们来简单介绍一个进程与程序的关系。所谓程序,不过是指可运行的二进制代码文件,把这种文件加载到内存中运行就得到了一个进程。同一个程序文件可以被加载多次成为不同的进程。因此,进程标识符和进程之间是一对一的关系,而与程序文件之间是多对一的关系。
在Linux shell中,可以使用ps命令查看当前用户所使用的进程。
[email protected]:~$ ps -u xiaomanon PID TTY TIME CMD 1296 ? 00:00:00 init 1357 ? 00:00:00 sh 1359 ? 00:00:00 sleep 1362 ? 00:00:01 dbus-daemon 1371 ? 00:00:00 upstart-event-b 1375 ? 00:00:00 window-stack-br 1383 ? 00:00:00 gnome-keyring-d 1393 ? 00:00:00 upstart-file-br 1410 ? 00:00:00 bamfdaemon 1414 ? 00:00:00 upstart-dbus-br 1418 ? 00:00:00 upstart-dbus-br 1419 ? 00:00:00 ibus-daemon 1428 ? 00:00:00 gvfsd 1460 ? 00:00:00 unity-settings- 1482 ? 00:00:00 hud-service 1492 ? 00:00:00 at-spi-bus-laun 1495 ? 00:00:00 gvfsd-fuse 1497 ? 00:00:00 gnome-session 1498 ? 00:00:00 dbus-daemon 1506 ? 00:00:00 ibus-dconf 1507 ? 00:00:00 ibus-ui-gtk3 1510 ? 00:00:01 unity-panel-ser 1512 ? 00:00:00 ibus-x11 1523 ? 00:00:00 at-spi2-registr 1600 ? 00:00:00 indicator-messa 1605 ? 00:00:00 indicator-bluet 1610 ? 00:00:00 indicator-keybo 1615 ? 00:00:00 indicator-power 1625 ? 00:00:00 indicator-datet 1627 ? 00:00:00 indicator-sound 1630 ? 00:00:00 indicator-print 1635 ? 00:00:00 indicator-sessi 1653 ? 00:00:00 indicator-appli 1666 ? 00:00:00 evolution-sourc 1680 ? 00:00:00 pulseaudio 1733 ? 00:00:00 ibus-engine-sim 1793 ? 00:00:00 dconf-service 1795 ? 00:00:00 notify-osd 1860 ? 00:00:05 compiz 1901 ? 00:00:00 evolution-calen 1916 ? 00:00:00 unity-fallback- 1929 ? 00:00:00 nautilus 1933 ? 00:00:00 polkit-gnome-au 1937 ? 00:00:01 nm-applet 1945 ? 00:00:00 vmtoolsd 1998 ? 00:00:00 gvfs-udisks2-vo 2023 ? 00:00:00 gvfs-mtp-volume 2029 ? 00:00:00 gvfs-afc-volume 2033 ? 00:00:00 gconfd-2 2036 ? 00:00:00 gvfs-gphoto2-vo 2059 ? 00:00:00 gvfsd-trash 2078 ? 00:00:00 gvfsd-burn 2100 ? 00:00:00 gvfsd-metadata 2105 ? 00:00:00 telepathy-indic 2112 ? 00:00:00 mission-control 2118 ? 00:00:00 signon-ui 2126 ? 00:00:00 gnome-terminal 2132 ? 00:00:00 gnome-pty-helpe 2133 pts/1 00:00:00 bash 2182 ? 00:00:00 zeitgeist-datah 2187 ? 00:00:00 zeitgeist-daemo 2193 ? 00:00:00 zeitgeist-fts 2197 ? 00:00:00 cat 2207 pts/1 00:00:00 ps
第一列内容是进程标识符(PID),这个标识符是唯一的;最后一列内容是进程的程序文件名。我们可以从中间找到有多个进程对应同一个程序文件名的情况,这是因为有一些常用的程序被多次运行了,比如shell和vi编辑器等。
注意:如果ps命令不使用“-u 用户名”作为参数,将不能检查到后台运行的进程。
[email protected]:~$ ps PID TTY TIME CMD 2133 pts/1 00:00:00 bash 2325 pts/1 00:00:00 ps
2 进程中重要的标识符
每个进程都有6个重要的ID值,分别是:进程ID、父进程ID、有效用户ID、有效组ID、实际用户ID和实际组ID。这6个ID保存在内核中的数据结构中,有些时候用户程序需要得到这些ID。
例如,在/proc文件系统中,每一个进程都拥有一个子目录,里面存有进程的信息。当使用进程读取这些文件时,应该先得到当前进程的ID才能确定进入哪一个进程的相关子目录。由于这些ID存储在内核之中,因此,Linux提供一组专门的接口函数来访问这些ID值。
Linux环境下分别使用getpid()和getppid()函数来得到进程ID和父进程ID,分别使用getuid()和geteuid()函数来得到进程的用户ID和有效用户ID,分别使用getgid()和getegid()来获得进程的组ID和有效组ID,其函数原型如下:
#include <unistd.h> pid_t getpid(void); //获取进程ID pid_t getppid(void); //获取父进程ID uid_t getuid(void); //获取用户ID uid_t geteuid(void); //获取有效用户ID gid_t getgid(void); //获取组ID gid_t getegid(void); //获取有效组ID
以上6个函数,如果执行成功,则返回对应的ID值;失败,则返回-1。除了进程ID和父进程ID这两个值不能够更改以外,其他的4个ID值在适当的条件下可以被更改。下面的示例程序用于获取当前进程的6个ID值并打印出来。
//Get ID information about current process #include <stdio.h> #include <unistd.h> int main(void) { printf("PID: %u\n", getpid()); printf("PPID: %u\n", getppid()); printf("UID: %u\n", getuid()); printf("EUID: %u\n", geteuid()); printf("GID: %u\n", getgid()); printf("EGID: %u\n", getegid()); return 0; }
程序运行效果如下:
[email protected]:~/Documents/c_code$ ./getid PID: 2681 PPID: 2133 UID: 1000 EUID: 1000 GID: 1000 EGID: 1000
3 参考文献
[1] 吴岳,Linux C程序设计大全,清华大学出版社