enum pid_type

本文详细介绍了Linux系统中进程ID(PID)的管理和表示方法,包括不同类型的ID(如线程组ID和会话ID),以及如何在命名空间环境中管理这些ID。

enum pid_type
{
 PIDTYPE_PID,//0,表示进程PID
 PIDTYPE_TGID,//1,表示线程组领头的进程的PID
 PIDTYPE_PGID,//2,表示进程组领头的进程的PID
 PIDTYPE_SID,//3,表示会话组领头的进程的PID
 PIDTYPE_MAX//4,
};

 

 

2.3.3 进程ID号(1)

UNIX进程总是会分配一个号码用于在其命名空间中唯一地标识它们。该号码被称作进程ID号,简称PID。用fork或clone产生的每个进程都由内核自动地分配了一个新的唯一的PID值。

1. 进程ID

但每个进程除了PID这个特征值之外,还有其他的ID。有下列几种可能的类型。

处于某个线程组(在一个进程中,以标志CLONE_THREAD来调用clone建立的该进程的不同的执行上下文,我们在后文会看到)中的所有进程都有统一的线程组ID(TGID)。如果进程没有使用线程,则其PID和TGID相同。

线程组中的主进程被称作组长(group leader)。通过clone创建的所有线程的task_struct的group_leader成员,会指向组长的task_struct实例。

另外,独立进程可以合并成进程组(使用setpgrp系统调用)。进程组成员的task_struct的pgrp属性值都是相同的,即进程组组长的PID。进程组简化了向组的所有成员发送信号的操作,这对于各种系统程序设计应用(参见系统程序设计方面的文献,例如[SR05])是有用的。请注意,用管道连接的进程包含在同一个进程组中。

几个进程组可以合并成一个会话。会话中的所有进程都有同样的会话ID,保存在task_struct的session成员中。SID可以使用setsid系统调用设置。它可以用于终端程序设计,但和我们这里的讨论不相干。

命名空间增加了PID管理的复杂性。回想一下,PID命名空间按层次组织。在建立一个新的命名空间时,该命名空间中的所有PID对父命名空间都是可见的,但子命名空间无法看到父命名空间的PID。但这意味着某些进程具有多个PID,凡可以看到该进程的命名空间,都会为其分配一个PID。 这必须反映在数据结构中。我们必须区分局部ID和全局ID。

全局ID是在内核本身和初始命名空间中的唯一ID号,在系统启动期间开始的init进程即属于初始命名空间。对每个ID类型,都有一个给定的全局ID,保证在整个系统中是唯一的。

局部ID属于某个特定的命名空间,不具备全局有效性。对每个ID类型,它们在所属的命名空间内部有效,但类型相同、值也相同的ID可能出现在不同的命名空间中。

全局PID和TGID直接保存在task_struct中,分别是task_struct的pid和tgid成员:

 
  1. <sched.h> 
  2. struct task_struct {  
  3. ...  
  4.         pid_t pid;  
  5.         pid_t tgid;  
  6. ...  

这两项都是pid_t类型,该类型定义为__kernel_pid_t,后者由各个体系结构分别定义。通常定义为int,即可以同时使用232个不同的ID。

会话和进程组ID不是直接包含在task_struct本身中,但保存在用于信号处理的结构中。task_ struct->signal->__session表示全局SID,而全局PGID则保存在task_struct->signal->__pgrp。辅助函数set_task_session和set_task_pgrp可用于修改这些值。

2. 管理PID

除了这两个字段之外,内核还需要找一个办法来管理所有命名空间内部的局部量,以及其他ID(如TID和SID)。这需要几个相互连接的数据结构,以及许多辅助函数,并将在下文讨论。

数据结构

下文我将使用ID指代提到的任何进程ID。在必要的情况下,我会明确地说明ID类型(例如,TGID,即线程组ID)。

一个小型的子系统称之为PID分配器(pid allocator)用于加速新ID的分配。此外,内核需要提供辅助函数,以实现通过ID及其类型查找进程的task_struct的功能,以及将ID的内核表示形式和用户空间可见的数值进行转换的功能。

在介绍表示ID本身所需的数据结构之前,我需要讨论PID命名空间的表示方式。我们所需查看的代码如下所示:

 
  1. <pid_namespace.h> 
  2. struct pid_namespace {  
  3. ...  
  4.         struct task_struct *child_reaper;  
  5. ...  
  6.         int level;  
  7.         struct pid_namespace *parent;  
  8. }; 

实际上PID分配器也需要依靠该结构的某些部分来连续生成唯一ID,但我们目前对此无需关注。我们上述代码中给出的下列成员更感兴趣。

每个PID命名空间都具有一个进程,其发挥的作用相当于全局的init进程。init的一个目的是对孤儿进程调用wait4,命名空间局部的init变体也必须完成该工作。child_reaper保存了指向该进程的task_struct的指针。

parent是指向父命名空间的指针,层次表示当前命名空间在命名空间层次结构中的深度。初始命名空间的level为0,该命名空间的子空间level为1,下一层的子空间level为2,依次递推。level的计算比较重要,因为level较高的命名空间中的ID,对level较低的命名空间来说是可见的。从给定的level设置,内核即可推断进程会关联到多少个ID。

回想图2-3的内容,命名空间是按层次关联的。这有助于理解上述的定义。

PID的管理围绕两个数据结构展开:struct pid是内核对PID的内部表示,而struct upid则表示特定的命名空间中可见的信息。两个结构的定义如下:

 
  1. <pid.h> 
  2. struct upid {  
  3.         int nr;  
  4.         struct pid_namespace *ns;  
  5.         struct hlist_node pid_chain;  
  6. };  
  7.  
  8. struct pid  
  9. {  
  10.         atomic_t count;  
  11.         /* 使用该pid的进程的列表 */  
  12.         struct hlist_head tasks[PIDTYPE_MAX];  
  13.         int level;  
  14.         struct upid numbers[1];  
  15. }; 

由于这两个结构与其他一些数据结构存在广泛的联系,在分别讨论相关结构之前,图2-5对此进行了概述。

对于struct upid,nr表示ID的数值,ns是指向该ID所属的命名空间的指针。所有的upid实例都保存在一个散列表中,稍后我们会看到该结构。pid_chain用内核的标准方法实现了散列溢出链表。

struct pid的定义首先是一个引用计数器count。tasks是一个数组,每个数组项都是一个散列表头,对应于一个ID类型。这样做是必要的,因为一个ID可能用于几个进程。所有共享同一给定ID的task_struct实例,都通过该列表连接起来。PIDTYPE_MAX表示ID类型的数目:

 
  1. <pid.h> 
  2. enum pid_type  
  3. {  
  4.         PIDTYPE_PID,  
  5.         PIDTYPE_PGID,  
  6.         PIDTYPE_SID,  
  7.         PIDTYPE_MAX  
  8. }; 

 
(点击查看大图)图2-5 实现可感知命名空间的ID表示所用的数据结构

 

``` /** ****************************(C) COPYRIGHT 2016 DJI**************************** * @file pid.c/h * @brief pid实现函数,包括初始化,PID计算函数, * @note * @history * Version Date Author Modification * V1.0.0 Dec-26-2018 RM 1. 完成 * @verbatim ============================================================================== ============================================================================== @endverbatim ****************************(C) COPYRIGHT 2016 DJI**************************** */ #ifndef PID_H #define PID_H #include "sys.h" typedef float fp32; typedef double fp64; enum PID_MODE { PID_POSITION = 0, PID_DELTA }; typedef struct { //PID运算模式 uint8_t mode; //PID 三个基本参数 fp32 Kp; fp32 Ki; fp32 Kd; fp32 max_out; //PID最大输出 fp32 max_iout; //PID最大积分输出 fp32 set; //PID目标值 fp32 fdb; //PID当前值 fp32 out; //三项叠加输出 fp32 Pout; //比例项输出 fp32 Iout; //积分项输出 fp32 Dout; //微分项输出 //微分项最近三个值 0最新 1上一次 2上上次 fp32 Dbuf[3]; //误差项最近三个值 0最新 1上一次 2上上次 fp32 error[3]; } rho_pid_type_def; typedef struct { //PID运算模式 uint8_t mode; //PID 三个基本参数 fp32 Kp; fp32 Ki; fp32 Kd; fp32 max_out; //PID最大输出 fp32 max_iout; //PID最大积分输出 fp32 set; //PID目标值 fp32 fdb; //PID当前值 fp32 out; //三项叠加输出 fp32 Pout; //比例项输出 fp32 Iout; //积分项输出 fp32 Dout; //微分项输出 //微分项最近三个值 0最新 1上一次 2上上次 fp32 Dbuf[3]; //误差项最近三个值 0最新 1上一次 2上上次 fp32 error[3]; } theta_pid_type_def; /** * @brief pid struct data init * @param[out] pid: PID struct data point * @param[in] mode: PID_POSITION: normal pid * PID_DELTA: delta pid * @param[in] PID: 0: kp, 1: ki, 2:kd * @param[in] max_out: pid max out * @param[in] max_iout: pid max iout * @retval none */ /** * @brief pid struct data init * @param[out] pid: PID结构数据指针 * @param[in] mode: PID_POSITION:普通PID * PID_DELTA: 差分PID * @param[in] PID: 0: kp, 1: ki, 2:kd * @param[in] max_out: pid最大输出 * @param[in] max_iout: pid最大积分输出 * @retval none */ extern void rho_PID_init(rho_pid_type_def *pid, uint8_t mode, const fp32 PID[3], fp32 max_out, fp32 max_iout); extern void theta_PID_init(theta_pid_type_def *pid, uint8_t mode, const fp32 PID[3], fp32 max_out, fp32 max_iout); /** * @brief pid calculate * @param[out] pid: PID struct data point * @param[in] ref: feedback data * @param[in] set: set point * @retval pid out */ /** * @brief pid计算 * @param[out] pid: PID结构数据指针 * @param[in] ref: 反馈数据 * @param[in] set: 设定值 * @retval pid输出 */ extern fp32 rho_PID_calc(rho_pid_type_def *pid, fp32 ref, fp32 set); extern fp32 theta_PID_calc(theta_pid_type_def *pid, fp32 ref, fp32 set); /** * @brief pid out clear * @param[out] pid: PID struct data point * @retval none */ /** * @brief pid 输出清除 * @param[out] pid: PID结构数据指针 * @retval none */ //extern void PID_clear(pid_type_def *pid); int speed(int exspeed,int acspeed); #endif```分析
03-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值