在 Linux 中有很多进程在同时运行,可以使用两种方式对这些进程进行标识:
- 进程描述符的地址
- 进程标识符
对于当前操作系统中的每个独立进程来说,其对应的进程描述符的地址以及进程标识符都是唯一的。
1. 进程描述符
为了对进程进行管理,Linux 内核必须了解每个进程当前的执行状态,这些状态包括进程的优先级、进程的运行状态、进程分配的地址空间等。因此,Linux 内核提供了一个 task_struct 类型的结构体进程描述符(process descriptor)来存放这些相关信息。
Linux 内核提供了一个数组 task 用于存放进程描述符,其包含指向系统中所有 task_struct 结构的指针。系统中的最大进程数目受 task 数组大小的限制,缺省值一般为 512。
创建新进程时,Linux 将从系统内存中分配一个 task_struct 结构并将其加入 task 数组。当前运行进程的结构用 current 指针来指示。
以下是一个进程描述符的主要结构及其说明:
struct task struct
{
yolatile long state;
//进程的运行时状态,-1代表不可运行,0代表可运行,>0代表已停止。
unsigned int flags;
//flags 是进程当前的状态标识,具体说明如下:
//0x00000002 表示进程正在被创建
//0x00000004 表示进程正准备退出
//0x00000040 表示此进程被 fork 出,但是并没有执行 exec
//0x00000400 表示此进程由于其他进程发送相关信号而被杀死
unsigned int rt_priority;
//进程的运行优先级
truct list_head tasks;
//list_head 结构体
struct mm_struct *mm;
//内存使用的相关情况
int exit_state;
int exit_code, exit_signal;
pid_t pid;
//进程标识号
pid_t tgid;
//进程组号
struct task_struct *real_parent;
//real_parent 是该进程的“亲生父亲”,不管其是否被 “寄养”
struct task_struct *parent;
//parent 是该进程现在的父进程,有可能是“继父”
struct list_head children;
//children 指的是该进程孩子的链表,可以得到所有子进程的进程描述符
struct list_head sibling;
//sibling 是该进程兄弟的链表,也就是其父亲的所有孩子的链表,用法与 children 相似
struct task_struct *group_leader;
//主线程的进程描述符
struct list_head thread_group;
//进程所有线程的链表
处理器 time_t utime, stime;
//进程相关时间
struct timespec start_time;
struct timespec real_ start_time;
//进程启动时间
char comm[TASK_COMM_LEN];
//这个是该进程所有线程的链表
int link_count, total_link_count;
//文件系统信息计数
struct thread_struct thread;
//特定处理器下的状态
struct fs_struct *fs;
//文件系统相关信息结构体
struct files_struct * files;
//打开的文件相关信息结构体
struct signal_struct *signal;
struct sighand_struct sighand;
//信号相关信息的句柄
unsigned long timer_slack_ns;
unsigned long default_timer_slack_ns;
//松弛时间值,用来规定 select() 和 poll() 的超时时间,单位是纳秒
};
2. 进程标识符
进程标识符(Process ID)是进程描述符中最重要的组成部分,是在当前 Linux 系统中唯一的一个非负整数,用于标识和对应唯一的进程。
Linux 内核使用了一个数据类型 pid_t 来存放进程的进程标识符,这个数据类型的实质是一个32位的无符号整型数据。进程标识符被顺序编号,通常来说是前一个进程的进程标识符的值加1。进程标识符可以重复使用,当一个进程被回收之后,过一段时间其标识符又可以被再次使用。Linux 内核上通常允许使用的进程标识符是 0~32767。
在Linux中,有几个特殊的进程标识符所对应的进程。
- 进程标识符0:对应的是交换进程(swapper),用于执行多进程的调用。
- 进程标识符1:对应的是初始化进程(init),在自举过程结束时由内核调用,对应的文件是/sbin/init,负责 Linux 的启动工作,这个进程在系统运行过程中是不会终止的,可以说当前操作系统中的所有进程都是这个进程衍生而来的。
- 进程标识符2:可能对应页守护进程(pagedaemon),用于虚拟存储系统的分页操作。
使用命令 ps -aux 可以查看系统中当前正在运行的进程的进程标识符以及其他一些信息。
可以使用 getpid 系列函数来获得当前进程的进程标识符。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
- getpid 函数用于获得当前调用进程的进程标识符。
- getppid 用于获得当前调用进程的父进程的进程标识符。
3. Linux进程的用户
进程也有对应的实际用户ID、实际组ID、有效用户ID、有效组ID,对于这些用户而言,每个进程同样存在一个相应的标识符,Linux 提供了相应的函数用于获取这些标识符。
#include <unistd.h>
#include <sys/types.h>
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
-
getuid:返回进程的实际用户标志符。
-
geteuid:返回调用进程的有效用户标识符。
-
getgid:返回调用进程的实际组标识符。
-
getegid:返回调用进程的有效组标识符。