购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.2 进程描述符

从一个进程诞生到退出的时间里,内核的进程管理子系统执行了各种操作,从进程创建、分配CPU时间、事件通知到进程终止时销毁进程。

除了地址空间之外,一个进程在内存中还被分配了一个称为进程描述符的数据结构,内核用它来识别、管理和调度该进程。图1-4描述了内核中的进程地址空间及其进程描述符。

图1-4

在Linux中,一个进程描述符是<linux/sched.h>中定义的struct task_struct类型的一个实例,它是核心数据结构之一,包含一个进程所拥有的所有属性、标识的详细信息和资源分配条目。查看struct task_struct就像是窥探内核在管理和调度进程时所看到或所使用的内容。

由于任务结构体包含一系列广泛的数据元素,这些元素与不同的内核子系统的功能相关,因此在本章中我们将单独探讨所有元素的目的和范围。我们将介绍一些与进程管理相关的重要元素。

1.2.1 进程属性:关键元素

进程属性定义了一个进程的所有关键特征和基本特征。这些元素包含进程的状态和标识以及其他重要的键值。

1.状态

一个进程从其产生之时起直至退出就一直处于不同的状态中,称为进程状态——它们定义了进程的当前状态。

TASK_RUNNING (0):任务正在执行或在调度器运行队列中争抢CPU。

TASK_INTERRUPTIBLE (1):任务处于可中断的等待状态;它仍然处于等待状态,直到所等待的条件变为真,例如互斥锁可用、I/O准备好的设备、睡眠时间超时,或者是一个专属的唤醒调用。在这个等待状态中,为进程生成的任何信号都被传递,使得等待条件被满足前唤醒进程。

TASK_KILLABLE :这与 TASK_INTERRUPTIBLE 类似,不同之处在于中断只能发生在致命信号上,这使它成为 TASK_INTERRUPTIBLE 以外更好的选择。

TASK_UNINTERRUPTIBLE (2):任务处于不可中断的等待状态,类似于 TASK_ INTERRUPTIBLE ,但是产生信号给这种睡眠进程不会导致其被唤醒。当它正在等待的事件发生时,进程才转换为 TASK_RUNNING 状态。该进程状态很少使用。

TASK_STOPPED (4):该任务已经收到停止(STOP)信号。在接收到继续信号(SIGCONT)后,它会回到运行状态。

TASK_TRACED (8):当一个进程可能正在被一个调试器仔细检查,便可认为它处于跟踪状态。

EXIT_ZOMBIE (32):该进程已经被终止,但它的资源尚未回收。

EXIT_DEAD (16):父进程使用wait方法收集子进程的退出状态后,子进程将终止,并且释放它所持有的所有资源。

图1-5描述了进程状态。

图1-5

2.pid

该字段保存了进程唯一的标识符,称为 PID。Linux中的PID是pid_t(整数)类型。虽然PID是一个整数,但通过/proc/sys/kernel/pid_max接口指定的默认最大值只有32 768。该文件中的值可以设置为任何值,最高可达2 22 (PID_MAX_LIMIT,约为400万)。

为了管理 PID,内核使用了位图。该位图允许内核跟踪PID的使用情况,并且可以为新进程分配唯一的PID。每个PID都是由PID位图中的一个位来标识的;PID的值是根据其对应位的位置来确定的。在位图中,值为1的位表示正在使用相应的PID,值为0的位表示空闲的PID。每当内核需要分配一个唯一的PID时,它就会查找第一个未被设置的位并将其设置为1,相反地,释放一个PID时,它会将相应的位从1设置为0。

3.tgid

该字段保存了线程组id。为了便于理解,假设创建了一个新进程,它的PID和TGID是相同的,因为进程恰好是唯一的线程。当进程产生一个新的线程时,新的子进程将获得唯一的PID,但是继承了父线程的TGID,因为它属于同一个线程组。TGID主要用于支持多线程进程。我们将在本章后面的线程部分深入了解。

4.thread info

该字段保存了处理器特定的状态信息,并且它是任务结构体的关键元素。本章后文会包含有关thread_info的重要细节。

5.flags

该标志字段记录了进程相应的各种属性。该字段中的每一位对应于一个进程生命周期中的各个阶段。每个进程标志定义在<linux/sched.h>中。

#define PF_EXITING           /* getting shut down */
#define PF_EXITPIDONE        /* pi exit done on shut down */
#define PF_VCPU              /* I'm a virtual CPU */
#define PF_WQ_WORKER         /* I'm a workqueue worker */
#define PF_FORKNOEXEC        /* forked but didn't exec */
#define PF_MCE_PROCESS       /* process policy on mce errors */
#define PF_SUPERPRIV         /* used super-user privileges */
#define PF_DUMPCORE          /* dumped core */
#define PF_SIGNALED          /* killed by a signal */
#define PF_MEMALLOC          /* Allocating memory */
#define PF_NPROC_EXCEEDED    /* set_user noticed that RLIMIT_NPROC was exceeded */
#define PF_USED_MATH         /* if unset the fpu must be initialized before use */
#define PF_USED_ASYNC       /* used async_schedule*(), used by module init */
#define PF_NOFREEZE         /* this thread should not be frozen */
#define PF_FROZEN           /* frozen for system suspend */
#define PF_FSTRANS          /* inside a filesystem transaction */
#define PF_KSWAPD           /* I am kswapd */
#define PF_MEMALLOC_NOIO0   /* Allocating memory without IO involved */
#define PF_LESS_THROTTLE    /* Throttle me less: I clean memory */
#define PF_KTHREAD          /* I am a kernel thread */
#define PF_RANDOMIZE        /* randomize virtual address space */
#define PF_SWAPWRITE        /* Allowed to write to swap */
#define PF_NO_SETAFFINITY   /* Userland is not allowed to meddle with cpus_allowed */
#define PF_MCE_EARLY        /* Early kill for mce process policy */
#define PF_MUTEX_TESTER     /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP     /* Freezer should not count it as freezable */
#define PF_SUSPEND_TASK     /* this thread called freeze_processes and should not be 
frozen */

6.exit_code和exit_signal

这些字段保存了任务的退出值和导致终止的信号的详细信息。这些字段将由父进程在子进程终止时通过wait()访问。

7.comm

该字段保存了用于启动进程的二进制可执行文件的名称。

8.ptrace

当使用ptrace()系统调用使进程转为跟踪模式时,将启用并设置该字段。

1.2.2 进程关系:关键元素

每个进程都可以与父进程关联,并建立父子关系。同样,由同一进程产生的多个进程被称为兄弟进程。这些字段确定当前进程与另一个进程的关系。

1.real_parent和parent

这些是指向父任务结构体的指针。对于正常的进程,这两个指针都指向同一个task_struct。它们的区别仅在于使用posix线程实现的多线程进程。对于这种情况,real_parent指向父线程任务结构体,parent指向收到SIGCHLD信号的进程任务结构体。

2.children

这是指向子任务结构体链表的指针。

3.sibling

这是一个指向兄弟任务结构体链表的指针。

4.group_leader

这个指针指向进程组组长的任务结构体。

1.2.3 调度属性:关键元素

所有相互竞争的进程都必须拥有公平的CPU时间,这就要求基于时间片和进程优先级来调度。以下这些属性包含了调度器所需的必要信息,以帮助确定哪个进程在竞争时获得优先权。

1.prio和static_prio

prio帮助确定调度进程的优先级。如果进程被分配了实时调度策略,则此字段保存了进程的静态优先级,范围为1~99(由sched_setscheduler()指定)。对于正常的进程,这个字段保存了由nice值得来的动态优先级。

2.se、rt和dl

每个任务都属于调度实体(任务组),因为调度是在每个实体级别上完成的。se用于所有正常进程,rt用于实时进程,dl用于截止期进程。我们将在下一章讨论关于调度的这些属性的更多细节。

3.policy

该字段保存了和进程调度策略相关的信息,这有助于确定进程的优先级。

4.cpus_allowed

该字段指定了进程的CPU掩码。也就是说,在多处理器系统中,进程允许在哪个CPU上进行调度。

5.rt_priority

该字段用于指定实时调度策略的进程优先级。但对于非实时进程,该字段未被使用。

1.2.4 进程限制:关键元素

内核施加资源限制以确保在相互竞争的进程中公平分配系统资源。这些限制保证了任意一个进程都不会独占所有的资源。有16种不同类型的资源限制,task structure指向一个struct rlimit类型的数组,其中每个偏移量包含了一个特定资源的当前值和最大值。

/*include/uapi/linux/resource.h*/
struct rlimit {
  __kernel_ulong_t        rlim_cur;
  __kernel_ulong_t        rlim_max;
};

这些限制在include/uapi/asm-generic/resource.h中进行了指定。

#define RLIMIT_CPU        0        /* CPU time in sec */
#define RLIMIT_FSIZE      1        /* Maximum filesize */
#define RLIMIT_DATA       2        /* max data size */
#define RLIMIT_STACK      3        /* max stack size */
#define RLIMIT_CORE       4        /* max core file size */
#ifndef RLIMIT_RSS
# define RLIMIT_RSS       5        /* max resident set size */
#endif
#ifndef RLIMIT_NPROC
# define RLIMIT_NPROC     6        /* max number of processes */
#endif
#ifndef RLIMIT_NOFILE
# define RLIMIT_NOFILE    7        /* max number of open files */
#endif
#ifndef RLIMIT_MEMLOCK
# define RLIMIT_MEMLOCK   8        /* max locked-in-memory
address space */
#endif
#ifndef RLIMIT_AS
# define RLIMIT_AS        9        /* address space limit */
#endif
#define RLIMIT_LOCKS      10       /* maximum file locks held */
#define RLIMIT_SIGPENDING 11       /* max number of pending signals */
#define RLIMIT_MSGQUEUE   12       /* maximum bytes in POSIX mqueues */
#define RLIMIT_NICE       13       /* max nice prio allowed to
raise to 0-39 for nice level  19 .. -20 */
#define RLIMIT_RTPRIO     14       /* maximum realtime priority */
#define RLIMIT_RTTIME     15       /* timeout for RT tasks in us */
#define RLIM_NLIMITS      16

1.2.5 文件描述符表:关键元素

在进程的生命周期中,它可以访问各种资源文件来完成其任务。这会导致进程打开、关闭、读取和写入这些文件。而系统又必须跟踪这些行为;文件描述符元素可以帮助系统了解进程操作了哪些文件。

1.fs

文件系统信息存储在该字段中。

2.files

文件描述符表保存了一些指针,这些指针指向进程为了执行各种操作而打开的所有文件。而files字段保存了一个指向该文件描述符表的指针。

1.2.6 信号描述符:关键元素

对于要处理信号的进程,任务结构体中有各种元素,而这些元素决定着信号必须如何处理。

1.signal

这是struct signal_struct类型的元素,它保存了与进程相关的所有信号的信息。

2.sighand

这是struct sighand_struct类型的元素,它保存了与进程相关的所有信号的处理函数。

3.sigset_t blocked和real_blocked

这些元素标识了当前被进程屏蔽或阻塞的信号。

4.pending

这是struct sigpending 类型的,它用来标识已经生成但尚未传递的信号。

5.sas_ss_sp

该字段保存了一个指向备用堆栈的指针,它有助于信号处理。

6.sas_ss_size

该字段表示用于信号处理的备用堆栈的大小。 nrqRVpF8ZUMmEway1L8fqZJNEiBdEN3f0BySGsO0ZehyDEPQMmipNHLaks1o1M8i

点击中间区域
呼出菜单
上一章
目录
下一章
×