进程标识符(PID)是一个进程的基本属性,其作用类似于每个人的身份证号码。根据进程标识符,用户可以精确地定位一个进程。一个进程标识符唯一对应一个进程,而多个进程标识符可以对应同一个程序。本文将深入探讨进程标识符及其相关操作。
1 进程标识符
每个进程在系统中都有唯一的一个ID标识它,这个ID就是进程标识符(PID)。因为其唯一,所以系统可以根据它准确定位到一个进程。进程标识符的类型为pid_t,其本质上是一个无符号整型的类型别名(typedef)。
接下来,我们来简单介绍一个进程与程序的关系。所谓程序,不过是指可运行的二进制代码文件,把这种文件加载到内存中运行就得到了一个进程。同一个程序文件可以被加载多次成为不同的进程。因此,进程与进程标识符之间是一对一的关系,而与程序文件之间是多对一的关系。
在Linux shell中,可以使用ps命令查看当前用户所使用的进程。
xiaomanon@xiaomanon-machine:~$ 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 用户名”作为参数,将不能检查到后台运行的进程。
xiaomanon@xiaomanon-machine:~$ 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;
}
程序运行效果如下:
xiaomanon@xiaomanon-machine:~/Documents/c_code$ ./getid
PID: 2681
PPID: 2133
UID: 1000
EUID: 1000
GID: 1000
EGID: 1000
3 参考文献
[1] 吴岳,Linux C程序设计大全,清华大学出版社