我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Linux的进程ID号怎么实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Linux的进程ID号怎么实现

这篇文章主要介绍“Linux的进程ID号怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Linux的进程ID号怎么实现”文章能帮助大家解决问题。

Linux的进程ID号怎么实现

  本文中的代码摘自 Linux内核5.15.13版本。

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

一、进程ID

1.1、其他ID

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

  1、 处于某个线程组(在一个进程中,以标志CLONE_THREAD来调用clone建立的该进程的不同的执行上下文,我们在后文会看到)中的所有进程都有统一的线程组ID( TGID)。如果进程没有使用线程,则其PID和TGID相同。线程组中的主进程被称作组长( group leader)。通过clone创建的所有线程的task_struct的group_leader成员,会指向组长的task_struct实例。

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

  3、 几个进程组可以合并成一个会话。会话中的所有进程都有同样的会话ID,保存在task_struct的session成员中。 SID可以使用setsid系统调用设置。它可以用于终端程序设计。

1.2、全局ID和局部ID

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

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

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

1.3、ID实现

  全局PID和TGID直接保存在task_struct中,分别是task_struct的pid和tgid成员,在sched.h文件里:

struct task_struct {...pid_t pid;pid_t tgid;...}

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

二、管理PID

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

2.1、PID命名空间的表示方式

  在pid_namespace.h文件内有如下定义:

struct pid_namespace {struct idr idr;struct rcu_head rcu;unsigned int pid_allocated;struct task_struct *child_reaper;struct kmem_cache *pid_cachep;unsigned int level;struct pid_namespace *parent;#ifdef CONFIG_BSD_PROCESS_ACCTstruct fs_pin *bacct;#endifstruct user_namespace *user_ns;struct ucounts *ucounts;int reboot;struct ns_common ns;} __randomize_layout;

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

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

2.2、PID的管理

2.2.1、PID的数据结构

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

struct upid {int nr;struct pid_namespace *ns;};struct pid{refcount_t count;unsigned int level;spinlock_t lock;struct hlist_head tasks[PIDTYPE_MAX];struct hlist_head inodes;wait_queue_head_t wait_pidfd;struct rcu_head rcu;struct upid numbers[1];};

  对于struct upid, nr表示ID的数值, ns是指向该ID所属的命名空间的指针。所有的upid实例都保存在一个散列表中。 pid_chain用内核的标准方法实现了散列溢出链表。struct pid的定义首先是一个引用计数器count。 tasks是一个数组,每个数组项都是一个散列表头,对应于一个ID类型。这样做是必要的,因为一个ID可能用于几个进程。所有共享同一给定ID的task_struct实例,都通过该列表连接起来。 PIDTYPE_MAX表示ID类型的数目:

enum pid_type{PIDTYPE_PID,PIDTYPE_TGID,PIDTYPE_PGID,PIDTYPE_SID,PIDTYPE_MAX,};
2.2.2、PID与进程的联系

  一个进程可能在多个命名空间中可见,而其在各个命名空间中的局部ID各不相同。 level表示可以看到该进程的命名空间的数目(换言之,即包含该进程的命名空间在命名空间层次结构中的深度),而numbers是一个upid实例的数组,每个数组项都对应于一个命名空间。注意该数组形式上只有一个数组项,如果一个进程只包含在全局命名空间中,那么确实如此。由于该数组位于结构的末尾,因此只要分配更多的内存空间,即可向数组添加附加的项。

  由于所有共享同一ID的task_struct实例都按进程存储在一个散列表中,因此需要在struct task_struct中增加一个散列表元素在sched.h文件内进程的结构头定义内有

struct task_struct {...struct pid*thread_pid;struct hlist_nodepid_links[PIDTYPE_MAX];struct list_headthread_group;struct list_headthread_node;...};

  将task_struct连接到表头在pid_links中的散列表上。

2.2.3、查找PID

  假如已经分配了struct pid的一个新实例,并设置用于给定的ID类型。它会如下附加到task_struct,在kernel/pid.c文件内:

static struct pid **task_pid_ptr(struct task_struct *task, enum pid_type type){return (type == PIDTYPE_PID) ?&task->thread_pid :&task->signal->pids[type];}void attach_pid(struct task_struct *task, enum pid_type type){struct pid *pid = *task_pid_ptr(task, type);hlist_add_head_rcu(&task->pid_links[type], &pid->tasks[type]);}

  这里建立了双向连接: task_struct可以通过task_struct->pids[type]->pid访问pid实例。而从pid实例开始,可以遍历tasks[type]散列表找到task_struct。 hlist_add_head_rcu是遍历散列表的标准函数。

三、生成唯一的PID

  除了管理PID之外,内核还负责提供机制来生成唯一的PID。为跟踪已经分配和仍然可用的PID,内核使用一个大的位图,其中每个PID由一个比特标识。 PID的值可通过对应比特在位图中的位置计算而来。因此,分配一个空闲的PID,本质上就等同于寻找位图中第一个值为0的比特,接下来将该比特设置为1。反之,释放一个PID可通过将对应的比特从1切换为0来实现。在建立一个新进程时,进程可能在多个命名空间中是可见的。对每个这样的命名空间,都需要生成一个局部PID。这是在alloc_pid中处理的,在文件kernel/pid.c内有:

struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid,      size_t set_tid_size){struct pid *pid;enum pid_type type;int i, nr;struct pid_namespace *tmp;struct upid *upid;int retval = -ENOMEM;if (set_tid_size > ns->level + 1)return ERR_PTR(-EINVAL);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--) {int tid = 0;if (set_tid_size) {tid = set_tid[ns->level - i];retval = -EINVAL;if (tid < 1 || tid >= pid_max)goto out_free;if (tid != 1 && !tmp->child_reaper)goto out_free;retval = -EPERM;if (!checkpoint_restore_ns_capable(tmp->user_ns))goto out_free;set_tid_size--;}idr_preload(GFP_KERNEL);spin_lock_irq(&pidmap_lock);if (tid) {nr = idr_alloc(&tmp->idr, NULL, tid,       tid + 1, GFP_ATOMIC);if (nr == -ENOSPC)nr = -EEXIST;} else {int pid_min = 1;if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS)pid_min = RESERVED_PIDS;nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min,      pid_max, GFP_ATOMIC);}spin_unlock_irq(&pidmap_lock);idr_preload_end();if (nr < 0) {retval = (nr == -ENOSPC) ? -EAGAIN : nr;goto out_free;}pid->numbers[i].nr = nr;pid->numbers[i].ns = tmp;tmp = tmp->parent;}retval = -ENOMEM;get_pid_ns(ns);refcount_set(&pid->count, 1);spin_lock_init(&pid->lock);for (type = 0; type < PIDTYPE_MAX; ++type)INIT_HLIST_HEAD(&pid->tasks[type]);init_waitqueue_head(&pid->wait_pidfd);INIT_HLIST_HEAD(&pid->inodes);upid = pid->numbers + ns->level;spin_lock_irq(&pidmap_lock);if (!(ns->pid_allocated & PIDNS_ADDING))goto out_unlock;for ( ; upid >= pid->numbers; --upid) {idr_replace(&upid->ns->idr, pid, upid->nr);upid->ns->pid_allocated++;}spin_unlock_irq(&pidmap_lock);return pid;out_unlock:spin_unlock_irq(&pidmap_lock);put_pid_ns(ns);out_free:spin_lock_irq(&pidmap_lock);while (++i <= ns->level) {upid = pid->numbers + i;idr_remove(&upid->ns->idr, upid->nr);}if (ns->pid_allocated == PIDNS_ADDING)idr_set_cursor(&ns->idr, 0);spin_unlock_irq(&pidmap_lock);kmem_cache_free(ns->pid_cachep, pid);return ERR_PTR(retval);}

关于“Linux的进程ID号怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Linux的进程ID号怎么实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Linux的进程ID号怎么实现

这篇文章主要介绍“Linux的进程ID号怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Linux的进程ID号怎么实现”文章能帮助大家解决问题。  本文中的代码摘自 Linux内核5.15.1
2023-06-29

linux怎么根据进程id获取进程名

在Linux系统中,可以使用以下命令根据进程ID获取进程名:```shellps -p -o comm=```其中,``是要查询的进程ID。该命令使用`ps`命令,并通过`-p`选项指定要查询的进程ID,然后使用`-o comm=`选项指
2023-09-28

Linux的进程ID空间是什么

在Linux系统中,进程ID(Process ID)空间是一个整数范围,用来唯一标识每个正在运行的进程。在大多数Linux系统上,进程ID的取值范围是0到32767(包括0和32767),其中0保留给调度进程(内核线程),1保留给init进
Linux的进程ID空间是什么
2024-04-27

Linux的终端与进程怎么实现

这篇文章主要介绍“Linux的终端与进程怎么实现”,在日常操作中,相信很多人在Linux的终端与进程怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Linux的终端与进程怎么实现”的疑惑有所帮助!接下来
2023-06-16

Linux进程间通信怎么实现

这篇文章主要讲解了“Linux进程间通信怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux进程间通信怎么实现”吧!共享内存共享内存可以说是最有用的进程间通信方式,也是最快的IP
2023-07-05

linux命令grep怎么看进程号

使用grep命令查找进程号的一种常见方式是使用ps命令和管道操作符将进程列表传递给grep命令进行过滤。下面是具体步骤:1. 打开终端。2. 运行以下命令以查找进程号:```ps aux | grep 进程名```其中,“进程名”是你要查找
2023-09-27

Linux中怎么查看nginx进程号

要查看Nginx进程号,可以使用以下方法之一:使用pgrep命令查找Nginx进程号:pgrep nginx使用ps命令结合grep过滤器查找Nginx进程号:ps aux | grep nginx查看Nginx主进程号(通常在nginx.
Linux中怎么查看nginx进程号
2024-03-06

linux怎么查看进程端口号

要查看Linux系统上的进程端口号,可以使用以下命仗:使用netstat命令:netstat -tuln该命令将显示所有正在监听的TCP和UDP端口。使用ss命令:ss -tulnss命令也可以显示正在监听的TCP和UDP端口号。使用lso
linux怎么查看进程端口号
2024-03-14

linux怎么查看进程号和服务

在Linux中,可以使用以下命令来查看进程号和服务:查看正在运行的进程:`ps aux`:显示所有正在运行的进程。`ps -ef`:显示所有正在运行的进程。`top`:实时显示进程的状态。`htop`:类似于top,但提供更多的交互式
2023-10-25

linux中怎么使用grep看进程号

要使用grep命令来查看进程号,可以按照以下步骤进行操作:1. 打开终端窗口。2. 使用以下命令来获取进程列表:```ps aux```这将显示当前正在运行的所有进程及其相关信息。3. 使用管道符号 `|` 将ps命令的输出传递给grep命
2023-09-15

linux怎么查看端口号对应的进程

要查看端口号对应的进程,在Linux系统上可以使用以下命令:使用netstat命令查看端口号对应的进程:netstat -tulnp | grep 其中,-t参数表示显示TCP端口,-u参数表示显示UDP端口,-l参数表示显示监听
linux怎么查看端口号对应的进程
2024-03-12

Linux怎么查看进程占用端口号

在Linux系统中,可以使用以下命令来查看进程占用的端口号:1. 使用 netstat 命令:```netstat -tlnp```-t:只显示 TCP 协议相关的连接-l:只显示处于监听状态的连接-n:以数字形式显示端口号-p:显示进程
2023-08-25

oracle怎么实现表的id自增

Oracle中实现表的ID自增通常使用序列(Sequence)和触发器(Trigger)结合的方式来实现。下面是具体的步骤:创建一个序列(Sequence),用来生成自增的ID值。序列可以使用以下语句创建:CREATE SEQUENCE t
oracle怎么实现表的id自增
2024-04-09

LINUX怎么实现多线程进行cp复制

这篇文章主要为大家展示了“LINUX怎么实现多线程进行cp复制”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“LINUX怎么实现多线程进行cp复制”这篇文章吧。关于这个问题,意义虽然有限因为一般来
2023-06-04

Python进程池怎么实现的

Python进程池可以通过使用`multiprocessing`模块中的`Pool`类来实现。下面是一个简单的示例,展示了如何使用进程池进行并行计算:import multiprocessing# 定义一个任务函数,用于计算平方def
2023-10-25

python多进程怎么实现的

python 多进程使用 multiprocessing 模块实现。关键类和函数包括:process(代表进程)、pool(管理进程和任务分配)、queue(进程间数据传递)、lock(共享资源同步)和 barrier(进程阻塞)。实现基于
python多进程怎么实现的
2024-05-22

linux中0号进程的含义是什么

这篇文章主要介绍“linux中0号进程的含义是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“linux中0号进程的含义是什么”文章能帮助大家解决问题。在linux中,0号进程是指idle进程,是
2023-07-05

Linux 进程通信之FIFO的实现

FIFO通信(first in first out)FIFO 有名管道,实现无血缘关系进程通信。创建一个管道的伪文件a.mkfifo testfifo 命令创建b.也可以使用函数int mkfifo(const char *pathname
2022-06-04

Node中的进程和线程怎么实现

这篇文章主要介绍了Node中的进程和线程怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Node中的进程和线程怎么实现文章都会有所收获,下面我们一起来看看吧。一、进程和线程1.1、专业性文字定义进程(Pr
2023-07-04

编程热搜

目录