Linux内核进程管理之进程ID

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

进程ID简介


一个进程实体对应一个task_struct结构体,关于这个结构体后续的文章会有介绍,这里我们只需要知道通过该结构体我们可以获得该进程使用的所有资源信息。
进程ID顾名思义就是进程的ID,一个进程有四种类型的ID,分别是:

  • PID:用于唯一标记一个进程

这句话在以前是完全无误的,但是现在还必须加上一个条件,那就是在一个命名空间内唯一标记一个进程。关于命令空间,下一个章节会有介绍

  • TGID:线程组ID

一个进程可能包含多个线程,组成一个线程组,线程组内所有的线程的TGID都等于组长的PID。

  • PGID:进程组ID。

多个进程可以组成一个进程组,一个进程组内所有进程的PGID都等于组长的PID。

SID:几个进程组可以组成一个会话组,会话组内所有的进程都有相同的SID。

PID命名空间介绍


Linux的命名空间是一种资源隔离机制,我们拿PID来做说明。
在这里插入图片描述
上图是3级namespace,上级和下级之间是父子关系。关于namespace有两点十分关键:

  1. 各个namespace之间的是相互隔离的,也就是说PID可以重复。如上图各个namespace都有PID为1的进程。
  2. 在下级namespace中创建的进程,在上级namespace中是可见的。如上图在level2 namespace中创建了进程PID=1,该进程在level 1 namespace中被映射为PID=3的进程,在level0 namespace中被映射为PID=5的进程。

PID数据结构


struct pid
{
	atomic_t count;
	unsigned int level;
	/* lists of tasks that use this pid */
	struct hlist_head tasks[PIDTYPE_MAX];
	struct rcu_head rcu;
	struct upid numbers[1];
};

以上是PID数据结构的定义,有三个成员很重要,下面依次解释:
level:表示该进程处于第几级namespace。
number:表示该进程对应的所有PID成员,比如上图中,进程处于level2 namespace,则该进程有三个PID成员。
tasks[PID_TYPE_MAX]:指向对应的task_struct结构体,以便根据PID找到对应的task_struct。

struct upid {
	/* Try to keep pid_chain in the same cacheline as nr for find_vpid */
	int nr;
	struct pid_namespace *ns;
	struct hlist_node pid_chain;
};

nr:该PID number
ns:该PID所在namespace
pid_chain:将该PID链接到pid_hash的某个成员上去。

为了进一步理清各个结构体之间的关系,我们假设如下场景:在level2 namespace创建了一个进程A,进程A与进程B,C同属于一个进程组,进程A是组长。下面是各个结构体之间的关系图:
在这里插入图片描述

PID相关函数


  1. 根据task struct获取pid
static inline struct pid *task_pid(struct task_struct *task)
{
   return task->pids[PIDTYPE_PID].pid;
}
  1. 根据task_struct获取线程组的ID
static inline struct pid *task_tgid(struct task_struct *task)
{
   return task->group_leader->pids[PIDTYPE_PID].pid;
}

线程组ID其实就是线程组组长的PID。
3. 根据task_struct获取进程组的PGID

static inline struct pid *task_tgid(struct task_struct *task)
{
   return task->group_leader->pids[PIDTYPE_PID].pid;
}

跟线程组组长的PGID一致。
4. 根据task_struct获取会话组TPID

static inline struct pid *task_session(struct task_struct *task)
{
   return task->group_leader->pids[PIDTYPE_SID].pid;
}

跟线程组组长的TGID一致。
5. 根据pid和namespace获取局部PID number

pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
{
	struct upid *upid;
	pid_t nr = 0;

	if (pid && ns->level <= pid->level) {
		upid = &pid->numbers[ns->level];
		if (upid->ns == ns)
			nr = upid->nr;
	}
	return nr;
}

首先判断pid在该namespace中是否可见,即判断ns的level是否小于pid的level。
然后遍历pid中的upid成员,找出ns一致的upid。

  1. 分配PID
struct pid *alloc_pid(struct pid_namespace *ns)
{
	struct pid *pid;
	enum pid_type type;
	int i, nr;
	struct pid_namespace *tmp;
	struct upid *upid;
	int retval = -ENOMEM;

	pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
	if (!pid)
		return ERR_PTR(retval);

	tmp = ns;
	pid->level = ns->level;
	for (i = ns->level; i >= 0; i--) {
		nr = alloc_pidmap(tmp);
		if (nr < 0) {
			retval = nr;
			goto out_free;
		}

		pid->numbers[i].nr = nr;
		pid->numbers[i].ns = tmp;
		tmp = tmp->parent;
	}

	if (unlikely(is_child_reaper(pid))) {
		if (pid_ns_prepare_proc(ns))
			goto out_free;
	}

	get_pid_ns(ns);
	atomic_set(&pid->count, 1);
	for (type = 0; type < PIDTYPE_MAX; ++type)
		INIT_HLIST_HEAD(&pid->tasks[type]);

	upid = pid->numbers + ns->level;
	spin_lock_irq(&pidmap_lock);
	if (!(ns->nr_hashed & PIDNS_HASH_ADDING))
		goto out_unlock;
	for ( ; upid >= pid->numbers; --upid) {
		hlist_add_head_rcu(&upid->pid_chain,
				&pid_hash[pid_hashfn(upid->nr, upid->ns)]);
		upid->ns->nr_hashed++;
	}
	spin_unlock_irq(&pidmap_lock);

	return pid;

out_unlock:
	spin_unlock_irq(&pidmap_lock);
	put_pid_ns(ns);

out_free:
	while (++i <= ns->level)
		free_pidmap(pid->numbers + i);

	kmem_cache_free(ns->pid_cachep, pid);
	return ERR_PTR(retval);
}

分配PID主要分两步:
第一步是在当前namespace以及所有的父namespace中分配PID number,并将信息存入upid。
第二部是将upid链接到pid_hash哈希数组上去。

  1. 根据pid number以及namespace找到pid
struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
{
	struct upid *pnr;

	hlist_for_each_entry_rcu(pnr,
			&pid_hash[pid_hashfn(nr, ns)], pid_chain)
		if (pnr->nr == nr && pnr->ns == ns)
			return container_of(pnr, struct pid,
					numbers[ns->level]);

	return NULL;
}

主要分三步:
第一步是根据pid number以及namespace索引到pid_hash数组中的某一个。
第二部是循环遍历找到pid number以及namespace匹配的upid。
第三步是根据upid计算得到pid。

本文参考博客:
https://www.cnblogs.com/hazir/p/linux_kernel_pid.html

Linux内核进程管理子系统有什么第九回 —— 进程主结构详解(5) 阅读详情

相关推荐

Zigbee开发环境搭建:从驱动安装到程序烧录全指南

本文提供了一份详尽的Zigbee开发环境搭建全指南,涵盖从驱动安装到程序烧录的完整流程。重点介绍了使用SmartRF Flash Programmer仿真器进行程序下载的关键步骤,并详细说明了USB转串口驱动的配置方法,帮助开发者快速构建稳定的Zigbee开发基础。

weixin_29195217的博客 369

Linux下获得进程id进程名字

1. 通过进程名字得到进程id。 pid_t getProcessPidbyName(char *name) {     FILE *fptr;     char *buf = new char[255];     char cmd[255] = {'\0'};     pid_t pid = -1;     sprintf(cmd,"pidof %s",name);    

RandyLeonard的专栏 3409

计算机组成原理课设

计算机组成原理课程设计

qq_63306482的博客 8019

linux进程调度的id,二十一、Linux 进程与信号---进程查看进程状态、进程调度进程状态变化、进程标识...

21.1 进程查看进程状态21.1.1 ps 指令ps 指令通常可以查看到进程ID进程的用户 ID进程状态进程的 Commandps:查看当前用户启动的进程 ps -ef:详细查看后台进程信息,可以用 ps -ef | more 进行分屏查看 ps -aux:当前运行的进程占用的CPU的时间,内存的时间 USER:进程的属组PID进程IDPPID:父进程%CPU:进程占用的CPU百...

weixin_39581945的博客 247

Linux 进程 ID(PID)| 查看 / 获取

……

u013669912的博客 1万+

Linux kernel如何获取PID

/获取线程pid描述符。// 传入进程任何一个线程pid, 并查找进程PID(就是进程中leader线程的PID)

1945

linux 查看进程id,Linux内核进程管理进程ID

进程ID简介一个进程实体对应一个task_struct结构体,关于这个结构体后续的文章会有介绍,这里我们只需要知道通过该结构体我们可以获得该进程使用的所有资源信息。进程ID顾名思义就是进程ID,一个进程有四种类型的ID,分别是:PID:用于唯一标记一个进程这句话在以前是完全无误的,但是现在还必须加上一个条件,那就是在一个命名空间内唯一标记一个进程。关于命令空间,下一个章节会有介绍TGID:线程组...

weixin_42168230的博客 1097

轻松看懂Linux 内核进程管理进程ID

如果考虑进程之间有复杂的关系,如线程组、进程组、会话组,这些组均有组ID,分别为 TGID、PGID、SID,所以原来的 task_struct 中pid_link 指向一个 pid 结构体需要增加几项,用来指向到其组长的 pid 结构体,相应的 struct pid 原本只需要指回其 PID 所属进程的task_struct,现在要增加几项,用来链接那些以该 pid 为组长的所有进程组内进程。在上图有四个命名空间,一个父命名空间衍生了两个子命名空间,其中的一个子命名空间又衍生了一个子命名空间。

m0_74282605的博客 505

Linux 内核进程管理进程ID

Linux 内核使用task_struct数据结构来关联所有与进程有关的数据结构,Linux 内核所有涉及到进程程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一。该数据结构在内核文件include/linux/sched.h中定义,在Linux 3.8 的内核中,该数据结构足足有 380 行之多,在这里我不可能逐项去描述其表示的含义,本篇文章只关注该数据结构如何来组...

~~ LINUX ~~ 339

进程IDLinux内核进程管理深入分析之进程ID

参考: https://www.cnblogs.com/hazir/p/linux_kernel_pid.html time : 2019.5.7 Linux 内核使用task_struct数据结构来关联所有与进程相关的数据结构,Linux内核所有涉及到进程程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一。该数据结构在内核文件include/linux/sched.h中...

qq_22613757的博客 1203

linux 内核 进程id,Linux 内核进程管理进程ID.doc

Linux 内核使用?task_struct?数据结构来关联所有与进程有关的数据结构,Linux 内核所有涉及到进程程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一。该数据结构在内核文件?include/linux/sched.h?中定义,在Linux 3.8 的内核中,该数据结构足足有 380 行之多,在这里我不可能逐项去描述其表示的含义,本篇文章只关注该数据结构如何来组...

weixin_28452161的博客 123

Linux 内核进程管理进程ID 。图解

http://www.cnblogs.com/hazir/tag/kernel/ Linux 内核进程管理进程ID Linux 内核使用task_struct数据结构来关联所有与进程有关的数据结构,Linux 内核所有涉及到进程程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一。该数据结构在内核文件include/linux/sched.h中定...

weixin_30663471的博客 98

Linux 内核进程管理进程ID【转】

转自:http://www.cnblogs.com/hazir/p/linux_kernel_pid.html Linux 内核使用task_struct数据结构来关联所有与进程有关的数据结构,Linux 内核所有涉及到进程程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一。该数据结构在内核文件include/linux/...

weixin_33958585的博客 99

删除进程id_Linux内核设计与实现(进程管理

Linux内核设计与实现—>进程管理进程描述符任务结构进程间通信1.管道:1.有名管道特性特点:1、可以用于不同进程间通信,不限于必须有亲缘关系。有名管道的使用1、管道的创建 =====》mkfifoint mkfifo(const char * filename,mode_t mode);功能:通过该函数可以创建一个有名管道的设备文件出来。参数:filename 要创建的管道名称+ 路径...

weixin_39979215的博客 134
上一篇: current_thread_info在ARM32和ARM64平台的不同点解析
下一篇: Linux内核进程管理进程切换之context_switch
liuhangtiant
博客等级 码龄15年 127粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值