linux的副linux查找进程的命令怎么标识

Hi,欢迎来到嵌入式培训高端品牌 - 华清远见教育集团,专注嵌入式工程师培养15年!
全国免费报名电话:400-611-6270
当前位置: >
> Linux 进程创建
Linux 进程创建
时间:作者:华清远见
在 Linux 内核内,进程是由相当大的一个称为 task_struct 的结构表示的。此结构包含所有表示此进程所必需的数据,此外,还包含了大量的其他数据用来统计(accounting)和维护与其他进程的关系(父和子)。下面给出了 task_struct 的一小部分。task_struct 位于 ./linux/include/linux/sched.h。
struct task_struct {
&&&&&&&&&&&&&&&&void *
&&&&&&&&&&&&&&&&
&&&&&&&&int prio, static_
&&&&&&&&struct list_
&&&&&&&&struct mm_struct *mm, *active_
&&&&&&&&pid_
&&&&&&&&&&&&&&&&pid_
&&&&&&&&struct task_struct *real_
&&&&&&&&char comm[TASK_COMM_LEN];
&&&&&&&&struct thread_
&&&&&&&&struct files_struct *
&&&&&&&&...
在task_struct中,可以看到几个预料之中的项,比如执行的状态、堆栈、一组标志、父进程、执行的线程(可以有很多)以及开放文件。对其做简单声明如下
&1& state 变量是一些表明任务状态的比特位。常见的状态有:
&&&&&&&&1.TASK_RUNNING 表示进程正在运行,或是排在运行队列中正要运行
&&&&&&&&2.TASK_INTERRUPTIBLE 表示进程正在休眠
&&&&&&&&3.TASK_UNINTERRUPTIBLE 表示进程正在休眠但不能叫醒
&&&&&&&&4.TASK_STOPPED 表示进程停止
注:这些标志的完整列表可以在 ./linux/include/linux/sched.h 内找到。
&2& flags 定义了很多指示符,表明进程是否正在被创建(PF_STARTING)或退出(PF_EXITING),或是进程当前是否在分配内存(PF_MEMALLOC)。
&3& 每个进程都会被赋予优先级(称为 static_prio),但进程的实际优先级是基于加载以及其他几个因素动态决定的。优先级值越低,实际的优先级越高。
&4& tasks 字段提供了链接列表的能力。它包含一个 prev 指针(指向前一个任务)和一个 next 指针(指向下一个任务)。
&5& 进程的地址空间由 mm 和 active_mm 字段表示。mm 代表的是进程的内存描述符,而 active_mm 则是前一个进程的内存描述符(为改进上下文切换时间的一种优化)。
&6& 可执行程序的名称(不包含路径)占用 comm(命令)字段。
&7& thread_struct 则用来标识进程的存储状态。此元素依赖于 Linux 在其上运行的特定架构,在 ./linux/include/asm-i386/processor.h 内有这样的一个例子。在此结构内,可以找到该进程自执行上下文切换后的存储(硬件注册表、程序计数器等)。
在很多情况下,进程都是动态创建并由一个动态分配的 task_struct 表示。当然 init 进程例外,它总是存在并由一个静态分配的 task_struct 表示。
Linux 内所有进程的分配有两种方式。第一种方式是通过一个哈希表,由 PID 值进行哈希计算得到;第二种方式是通过双链循环表。循环表非常适合于对任务列表进行迭代。由于列表是循环的,没有头或尾;但是由于 init_task 总是存在,所以可以将其用作继续向前迭代的一个锚点。
任务列表无法从用户空间访问,但该问题很容易解决,方法是以模块形式向内核内插入代码。例如通过如下代码,它会迭代任务列表并会提供有关每个任务的少量信息(name、pid 和 parent 名)。
struct task_struct *task = &init_
/* Walk through the task list, until we hit the init_task again */
&&&&&&&&do {
&&&&&&&&printk( KERN_INFO &*** %s [%d] parent %s\n&,
&&&&&&&&&&&&&&&&&&&&&&&&task-&comm, task-&pid, task-&parent-&comm );
} while ( (task = next_task(task)) != &init_task );
注意,还可以标识当前正在运行的任务。Linux 维护一个称为 current 的符号,代表的是当前运行的进程(类型是 task_struct)。为此可使用如下代码:
printk( KERN_INFO, &Current task is %s [%d]&, current-&comm, current-&pid );
Linux创建用户空间进程的情况与内核空间进程类似。二者底层机制是一致的,因为终都会依赖于一个名为 do_fork 的函数来创建新进程。
在创建内核线程时,内核会调用一个名为 kernel_thread 的函数(参见 ./linux/arch/i386/kernel/process.c),此函数执行某些初始化后会调用 do_fork。
在用户空间,一个程序会调用 fork,这会导致对名为 sys_fork 的内核函数的系统调用(参见 ./linux/arch/i386/kernel/process.c)。
do_fork 是进程创建的基础。可以在 ./linux/kernel/fork.c 内找到 do_fork 函数(以及相关函数 copy_process)。
do_fork 函数首先调用 alloc_pidmap,该调用会分配一个新的 PID。接下来,do_fork 检查调试器是否在跟踪父进程。如果是,在 clone_flags 内设置 CLONE_PTRACE 标志以做好执行 fork 操作的准备。之后 do_fork 函数还会调用 copy_process,向其传递这些标志、堆栈、注册表、父进程以及新分配的 PID。
新的进程在 copy_process 函数内作为父进程的一个副本创建。此函数能执行除启动进程之外的所有操作,启动进程在之后进行处理。copy_process 内的第一步是验证 CLONE 标志以确保这些标志是一致的。如果不一致,就会返回 EINVAL 错误。接下来,询问 Linux Security Module (LSM) 看当前任务是否可以创建一个新任务。
接下来,调用 dup_task_struct 函数(./linux/kernel/fork.c ),这会分配一个新 task_struct 并将当前进程的描述符复制到其内。在新的线程堆栈设置好后,一些状态信息也会被初始化,并且会将控制返回给 copy_process。控制回到 copy_process 后,除了其他几个限制和安全检查之外,还会执行一些常规管理,包括在新 task_struct 上的各种初始化。之后,会调用一系列复制函数来复制此进程的各个方面,比如复制开放文件描述符(copy_files)、复制符号信息(copy_sighand 和 copy_signal)、复制进程内存(copy_mm)以及终复制线程(copy_thread)。
之后,这个新任务会被指定给一个处理程序,同时对允许执行进程的处理程序进行额外的检查(cpus_allowed)。新进程的优先级从父进程的优先级继承后,执行一小部分额外的常规管理,而且控制也会被返回给 do_fork。在此时,新进程存在但尚未运行。do_fork 函数通过调用 wake_up_new_task 来修复此问题。此函数(./linux/kernel/sched.c )初始化某些调度程序的常规管理信息,将新进程放置在运行队列之内,然后将其唤醒以便执行。后,一旦返回至 do_fork,此 PID 值即被返回给调用程序,进程完成。
下一篇:没有了
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
学院新动态Linux进程标识
(1) 进程标识说明
每个进程都有一个非负整型的唯一进程ID。因为进程ID标识符总是唯一的,常将其用作其他标识符的一部分以保证其唯一性。
在Linux中,进程ID 0是调度进程,常常被称为交换进程。该进程并不执行任何磁盘上的程序—它是内核的一部分,因此也被称为系统进程。进程ID 1通常是init进程,在自举过程结束时由内核调用。
init通常读与系统有关的初始化文件(/etc/rc*文件),并将系统引导到一个状态 (例如多用户 ),init进程决不会终止,它是一个普通的用户进程 (与交换进程不同,它不是内核中的系统进程 ),但是它以超级用户特权态运行,init进程是所有孤儿进程的父进程。
在 Linux 中最主要的进程标识有进程号(PID,Process Idenity Number)和它的父进程号(PPID,parent process ID), 其中 PID 唯一地标识一个进程,PID 和 PPID 都是非零的正整数。
(2) 进程标识相关函数原型
#include &sys/types.h&
#include &unistd.h&
getpid(void )
getppid(void )
getuid(void )
实际用户ID
geteuid(void )
有效用户ID
getgid(void )
getegid(void )
struct passwd * getpwuid(uid_t uid)
用来逐一搜索参数uid 指定的用户识别码,找到时便将该用户的信息以passwd结构返回
(3) 进程标识函数使用实例
pid.c源代码如下:
#include &unistd.h&
#include &pwd.h&
#include &sys/types.h&
#include &stdio.h&
int main(int argc,char **argv)
pid_t my_pid,parent_
uid_t my_uid,my_
gid_t my_gid,my_
struct passwd *my_
my_pid=getpid();
parent_pid=getppid();
my_uid=getuid();
my_euid=geteuid();
my_gid=getgid();
my_egid=getegid();
my_info=getpwuid(my_uid);
printf("Process ID%ld/n",my_pid);
printf("Parent ID%ld/n",parent_pid);
printf("User ID%ld/n",my_uid);
printf("Effective User ID%ld/n",my_euid);
printf("Group ID%ld/n",my_gid);
printf("Effective Group ID%ld/n",my_egid)
if(my_info)
printf("My Login Name%s/n" ,my_info-&pw_name);
printf("My Password %s/n" ,my_info-&pw_passwd);
printf("My User ID %ld/n",my_info-&pw_uid);
printf("My Group ID %ld/n",my_info-&pw_gid);
printf("My Real Name %s/n" ,my_info-&pw_gecos);
printf("My Home Dir %s/n", my_info-&pw_dir);
printf("My Work Shell%s/n", my_info-&pw_shell);
编译 gcc pid.c –o pid。
执行 ./pid,执行结果如下:
Process ID6492
Parent ID5707
User ID1008
Effective User ID1008
Group ID1003
Effective Group ID1003
My Login Name zjkf
My Password x
My User ID 1008
My Group ID 1003
My Real Name zjkf,,,,
My Home Dir /home/zjkf
My Work Shell/bin/bash
摘录自《深入浅出Linux工具与编程》
没有更多推荐了,Linux系统如何标识进程?
其实两年前,本站已经有了一篇关于的文档,不过非常的简陋,而且代码是来自2.6内核。随着linux container、pid namespace等概念的引入,进程标识方面已经有了天翻地覆的变化,因此我们需要对这部分的内容进行重新整理。
本文主要分成四个部分来描述进程标识这个主题:在初步介绍了一些入门的各种IDs基础知识后,在第三章我们描述了pid、pid number、pid namespace等基础的概念。第四章重点描述了内核如何将这些基本概念抽象成具体的数据结构,最后我们简单分析了内核关于进程标识的源代码(代码来自linux4.4.6版本)。
二、各种ID概述
所谓进程其实就是执行中的程序而已,和静态的程序相比,进程是一个运行态的实体,拥有各种各样的资源:地址空间(未必使用全部地址空间,而是排布在地址空间上的一段段的memory mappings)、打开的文件、pending的信号、一个或者多个thread of execution,内核中数据实体(例如一个或者多个task_struct实体),内核栈(也是一个或者多个)等。针对进程,我们使用进程ID,也就是pid(process ID)。通过getpid和getppid可以获取当前进程的pid以及父进程的pid。
进程中的thread of execution被称作线程(thread),线程是进程中活跃状态的实体。一方面进程中所有的线程共享一些资源,另外一方面,线程又有自己专属的资源,例如有自己的PC值,用户栈、内核栈,有自己的hw context、调度策略等等。我们一般会说进程调度什么的,但是实际上线程才是是调度器的基本单位。对于Linux内核,线程的实现是一种特别的存在,和经典的unix都不一样。在linux中并不区分进程和线程,都是用task_struct来抽象,只不过支持多线程的进程是由一组task_struct来抽象,而这些task_struct会共享一些数据结构(例如内存描述符)。我们用thread ID来唯一标识进程中的线程,POSIX规定线程ID在所属进程中是唯一的,不过在linux kernel的实现中,thread ID是全系统唯一的,当然,考虑到可移植性,Application software不应该假设这一点。在用户空间,通过gettid函数可以获取当前线程的thread ID。对于单线程的进程,process ID和thread ID是一样的,对于支持多线程的进程,每个线程有自己的thread ID,但是所有的线程共享一个PID。
为了方便shell进行Job controll,我们需要把一组进程组织起来形成进程组。关于这方面的概念,在文档中描述的很详细,这里就不赘述了。为了标识进程组,我们需要引入进程组ID的概念。我们一般把进程组中的第一个进程的ID作为进程组的ID,进程组中的所有进程共享一个进程组ID。在用户空间,通过setpgid、getpgid、setpgrp和getpgrp等接口函数可以访问process group ID。
经过thread ID、process ID、process group ID的层层递进,我们终于来到最顶层的ID,也就是session ID,这个ID实际上是用来标识计算机系统中的一次用户交互过程:用户登录入系统,不断的提交任务(即Job或者说是进程组)给计算机系统并观察结果,最后退出登录,销毁该session。关于session的概念,在文档中描述的也很详细,大家可以参考那份文档,这里就不赘述了。在用户空间,我们可以通过getsid、setsid来操作session ID。
三、基础概念
1、用户空间如何看到process ID
我们用下面这个block diagram来描述用户空间和内核空间如何看待process ID的:
从用户空间来看,每一个进程都可以调用getpid来获取标识该进程的ID,我们称之PID,其类型是pid_t。因此,我们知道在用户空间可以通过一个正整数来唯一标识一个进程(我们称这个正整数为pid number)。在引入容器之后,事情稍微复杂一点,pid这个正整数只能是唯一标识容器内的进程。也就是说,如果有容器1和容器2存在于系统中,那么可以同时存在两个pid等于a的进程,分别位于容器1和容器2。当然,进程也可以不在容器里,例如进程x和进程y,它们就类似传统的linux系统中的进程。当然,你也可以认为进程x和进程y位于一个系统级别的顶层容器0,其中包括进程x和进程y以及两个容器。同样的概念,容器2中也可以嵌套一个容器,从而形成了一个container hierarchy。
容器(linux container)是一个OS级别的虚拟化方法,基本上是属于纯软件的方法来实现虚拟化,开销小,量级轻,当然也有自己的局限。linux container主要应用了内核中的cgroup和namespace隔离技术,当然这些内容不是我们这份文档关心的,我们这里主要关心pid namespace。
当一个进程运行在linux OS之上的时候,它拥有了很多的系统资源,例如pid、user ID、网络设备、协议栈、IP以及端口号、filesystem hierarchy。对于传统的linux,这些资源都是全局性的,一个进程umount了某一个文件系统挂载点,改变了自己的filesystem hierarchy视图,那么所有进程看到的文件系统目录结构都变化了(umount操作被所有进程感知到了)。有没有可能把这些资源隔离开呢?这就是namespace的概念,而PID namespace就是用来隔离pid的地址空间的。
进程是感知不到pid namespace的,它只是知道能够通过getpid获取自己的ID,并不知道自己实际上被关在一个pid namespace的牢笼。从这个角度看,用户空间是简单而幸福的,内核空间就没有这么幸运了,我们需要使用复杂的数据结构来抽象这些形成层次结构的PID。
最后顺便说一句,上面的描述是针对pid而言的,实际上,tid、pgid和sid都是一样的概念,原来直接使用这些ID就可以唯一标识一个实体,现在我们需要用(pid namespace,ID)来唯一标识一个实体。
2、内核空间如何看到process ID
虽然从用户空间看,一个pid用一个正整数表示就足够了,但是在内核空间,一个正整数肯定是不行的,我们用一个2个层次的pid namespace来描述(也就是上面图片的情形)。pid namespace 0是pid namespace 1和2的parent namespace,在pid namespace 1中的pid等于a的那进程,对应pid namespace 0中的pid等于m的那进程,也就是说,内核态实际需要两个不同namespace中的正整数来记录一个进程的ID信息。推广开来,我们可以这么描述,在一个n个level的pid namespace hieraray中,位于x level的进程需要x个正整数ID来表示该该进程。
除此之外,内核还有记录pid namespace之间的关系:谁是根,谁是叶,父子关系……
四、内核态的数据抽象
1、如何抽象pid number?
struct upid {
&&& struct pid_namespace *
&&& struct hlist_node pid_
虽然用户空间使用一个正整数来表示各种IDs,但是对于内核,我们需要使用(pid namespace,ID number)这样的二元组来表示,因为单纯的pid number是没有意义的,必须限定其pid namespace,只有这样,那个ID number才是唯一的。这样,upid中的nr和ns成员就比较好理解了,分别对应ID number和pid namespace。此外,当userspace传递ID number参数进入内核请求服务的时候(例如向某一个ID发送信号),我们必须需要通过ID number快速找到其对应的upid数据对象,为了应对这样的需求,内核将系统内所有的upid保存在哈希表中,pid_chain成员是哈希表中的next node。
2、如何抽象tid、pid、sid、pgid?
struct pid
&&& atomic_
&&& struct hlist_head tasks[PIDTYPE_MAX];
&&& struct rcu_
&&& struct upid numbers[1];
虽然其名字是pid,不过实际上这个数据结构抽象了不仅仅是一个thread ID或者process ID,实际上还包括了进程组ID和session ID。由于多个task struct会共享pid(例如一个session中的所有的task struct都会指向同一个表示该session ID的struct pid数据对象),因此存在count这样的成员也就不奇怪了,表示该数据对象的引用计数。
在了解了pid namespace hierarchy之后,level成员也不难理解,任何一个系统分配的PID都是隶属于某一个namespace的,而这个namespace又是位于整个pid namespace hierarchy的某个层次上,pid-&level指明了该PID所属的namespace的level。由于pid对其parent pid namespace也是可见的,因此,这个level值其实也就表示了这个pid对象在多少个pid namespace中可见。
在多少个pid namespace中可见,就会有多少个(pid namespace,pid number)对,numbers就是这样的一个数组,表示了在各个level上的pid number。tasks成员和使用该struct pid的task们关联,我们在下一节描述。
3、进程描述符中如何体现tid、pid、sid、pgid?
由于多个task共享ID(泛指上面说的四种ID),因此在设计数据结构的时候我们要考虑两种情况:
(1)从task struct快速找到对应的struct pid
(2)从struct pid能够遍历所有使用该pid的task
在这样的要求下,我们设计了一个辅助数据结构:
struct pid_link
&&& struct hlist_
&&& struct pid *
其中node是将task串接到struct pid的task struct链表中的节点,而pid指向具体的struct pid。这时候,我们可以在task struct中嵌入一个pid_link的数组:
struct task_struct {
struct pid_link pids[PIDTYPE_MAX];
Task struct中的pids成员是一个数组,分别表示该task的tid(pid)、pgid和sid。我们定义pid的类型如下:
enum pid_type
&&& PIDTYPE_PID,
&&& PIDTYPE_PGID,
&&& PIDTYPE_SID,
&&& PIDTYPE_MAX
一直以来我们都是说四种type,tid、pid、sid、pgid,为何这里少定义一种呢?其实开始版本的内核的确是定义了四种type的pid,但是后来为了节省内存,tid和pid合二为一了。OK,现在已经引入太多的数据结构,下面我们用一幅图片来描述数据结构之间的关系:
对于一个进程中的多个线程而言,每一个线程都可以通过task-&pids[PIDTYPE_PID].pid找到该线程对应的表示thread ID的那个struct pid数据对象。当然,任何一个线程都有其所属的进程,也就是有表示其process id的那个struct pid数据对象。如何找到它呢?这需要一个桥梁,也就是task struct中定义的thread group 成员(task-&group_leader),通过该指针,一个线程总是很容易的找到其对应的线程组leader,而线程组leader对应的pid就是该线程的process ID。因此,对于一个线程,其task-&group_leader-&pids[PIDTYPE_PID].pid就指向了表示其process id的那个struct pid数据对象。当然,对于线程组leader,其thread ID和process ID的struct pid数据对象是一个实体,对于非线程组leader的那些普通线程,其thread ID和process ID的struct pid数据对象指向不同的实体。
struct pid有三个链表头,如果该pid仅仅是标识一个thread ID,那么其pid链表头指向的链表中只有一个元素,就是使用该pid的task struct。如果该pid表示的是一个process ID,那么pid链表头指向的链表中多个task struct,每一个元素表示了属于该进程的线程的task struct,链表中第一个task struct是thread group leader。如果该pid并不表示一个process group ID或者session ID,那么struct pid中的pgid链表头和session链表头都是指向null。如果该pid表示一个process group ID的时候,其结构如下图所示:
对于那些multi-thread进程,内核有若干个task struct和进程对应,不过为了简单,在上面图片中,进程x 对应的task struct实际上是thread group leader对应的那个task struct。这些task struct的pgid指针(task-&pids[PIDTYPE_PGID].pid)指向了该进程组对应的struct pid数据对象。而该pid中的pgid链表头串联了所有使用该pid的task struct(仅仅是串联thread group leader对应的那些task struct),而链表中的第一个节点就是进程组leader。
session pid的概念是类似的,大家可以自行了解学习。
4、如何抽象 pid namespace?
好吧,这个有点复杂,暂时TODO吧。
五、代码分析
1、如何根据一个task struct得到其对应的thread ID?
static inline struct pid *task_pid(struct task_struct *task)
&&& return task-&pids[PIDTYPE_PID].
同样的道理,我们也可以很容易得到一个task对应的pgid和sid。process ID有一点绕,我们首先要找到该task的thread group leader对应的task,其实一个线程的thread group leader对应的那个task的thread ID就是该线程的process ID。
2、如何根据一个task struct得到当前的pid namespace?
struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
&&& return ns_of_pid(task_pid(tsk));
这个操作可以分成两步,第一步首先找到其对应的thread ID,然后根据thread ID找到当前的pid namespace,代码如下:
static inline struct pid_namespace *ns_of_pid(struct pid *pid)
&&& struct pid_namespace *ns = NULL;
&&& if (pid)
&&&&&&& ns = pid-&numbers[pid-&level].
一个struct pid实体是有层次的,对应了若干层次的(pid namespace,pid number)二元组,最顶层是root pid namespace,最底层(叶节点)是当前的pid namespace,pid-&level表示了当前的层次,因此pid-&numbers[pid-&level].ns说明的就是当前的pid namespace。
3、getpid是如何实现的?
当陷入内核后,我们很容易获取当前的task struct(根据sp_svc的值),这是起点,后续的代码如下:
static inline pid_t task_tgid_vnr(struct task_struct *tsk)
&&& return pid_vnr(task_tgid(tsk));
通过task_tgid可以获取该task对应的thread group leader的thread ID,其实也就是process ID。此外,通过task_active_pid_ns亦可以获取当前的pid namespace,有了这两个参数,可以调用pid_nr_ns获取该task对应的pid number:
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
&&& struct upid *
&&& pid_t nr = 0;
&&& if (pid && ns-&level &= pid-&level) {
&&&&&&& upid = &pid-&numbers[ns-&level];
&&&&&&& if (upid-&ns == ns)
&&&&&&&&&&& nr = upid-&
一个pid可以贯穿多个pid namespace,但是并非所有的pid namespace都可以检视pid,获取相应的pid number。因此,在代码的开始会进行验证,如果pid namespace的层次(ns-&level)低于pid当前的pid namespace的层次,那么直接返回0。如果pid namespace的level是OK的,那么要检查该namespace是不是pid当前的那个pid namespace,如果是,直接返回对应的pid number,否则,返回0。
对于gettid和getppid这两个接口,整体的概念是和getpid类似的,不再赘述。
4、给定线程ID number的情况下,如何找对应的task struct?
这里给定的条件包括ID number、当前的pid namespace,在这样的条件下如何找到对应的task呢?我们分成两个步骤,第一个步骤是先找到对应的struct pid,代码如下:
struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
&&& struct upid *
&&& 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;
整个系统有那么多的struct pid数据对象,每一个pid又有多个level的(pid namespace,pid number)对,通过pid number和namespace来找对应的pid是一件非常耗时的操作。此外,这样的操作是一个比较频繁的操作,一个简单的例子就是通过kill向指定进程(pid number)发送信号。正是由于操作频繁而且耗时,系统建立了一个全局的哈希链表来解决这个问题,pid_hash指向了若干(具体head的数量和内存配置有关)哈希链表头。这个哈希表用来通过一个指定pid namespace和id number,来找到对应的struct upid。一旦找了upid,那么通过container_of找到对应的struct pid数据对象。
第二步是从struct pid找到task struct,代码如下:
struct task_struct *pid_task(struct pid *pid, enum pid_type type)
&&& struct task_struct *result = NULL;
&&& if (pid) {
&&&&&&& struct hlist_node *
&&&&&&& first = rcu_dereference_check(hlist_first_rcu(&pid-&tasks[type]),
&&&&&&&&&&&&&&&&&&&&&&&&& lockdep_tasklist_lock_is_held());
&&&&&&& if (first)
&&&&&&&&&&& result = hlist_entry(first, struct task_struct, pids[(type)].node);
5、getpgid是如何实现的?
SYSCALL_DEFINE1(getpgid, pid_t, pid)
&&& struct task_struct *p;
&&& struct pid *
&&& rcu_read_lock();
&&& if (!pid)
&&&&&&& grp = task_pgrp(current);
&&& else {
&&&&&&& retval = -ESRCH;
&&&&&&& p = find_task_by_vpid(pid);
&&&&&&& if (!p)
&&&&&&&&&&&
&&&&&&& grp = task_pgrp(p);
&&&&&&& if (!grp)
&&&&&&&&&&&
&&&&&&& retval = security_task_getpgid(p);
&&&&&&& if (retval)
&&&&&&&&&&&
&&& retval = pid_vnr(grp);
&&& rcu_read_unlock();
当传入的pid number等于0的时候,getpgid实际上是获取当前进程的process groud ID number,通过task_pgrp可以获取该进程的使用的表示progress group ID对应的那个pid对象。如果调用getpgid的时候给出了非0的process ID number,那么getpgid实际上是想要获取指定pid number的gpid。这时候,我们需要调用find_task_by_vpid找到该pid number对应的task struct。一旦找到task struct结构,那么很容易得到其使用的pgid(该实体是struct pid类型)。至此,无论哪一种参数情况(传入的参数pid number等于0或者非0),我们都找到了该pid number对应的struct pid数据对象(pgid)。当然,最终用户空间需要的是pgid number,因此我们需要调用pid_vnr找到该pid在当前namespace中的pgid number。
getsid的代码逻辑和getpid是类似的,不再赘述。
原创文章,转发请注明出处。蜗窝科技

我要回帖

更多关于 linux查找进程的命令 的文章

 

随机推荐