• Linux进程管理之通过pid号找到struct task_struct


    一、find_get_pid

    1.1 API详解

    (1)
    此函数根据用户层提供的进程号 nr 获取对应的进程描述符struct pid,并使进程描述符中的字段count的值加1,即此进程的用户数加1。

    struct pid *find_get_pid(pid_t nr)
    {
    	struct pid *pid;
    
    	rcu_read_lock();
    	pid = get_pid(find_vpid(nr));
    	rcu_read_unlock();
    
    	return pid;
    }
    EXPORT_SYMBOL_GPL(find_get_pid);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (2)
    将进程描述符 struct pid的count成员原子的+1。

    static inline struct pid *get_pid(struct pid *pid)
    {
    	if (pid)
    		atomic_inc(&pid->count);
    	return pid;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (3)
    根据提供的局部进程号获取对应的进程描述符 struct pid :
    参数nr是进程对应的局部进程号,一般与进程号相同。
    参数ns是struct pid_namespace型变量,是对进程命名空间信息的描述。

    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;
    }
    EXPORT_SYMBOL_GPL(find_pid_ns);
    
    // 参数nr是进程对应的局部进程号,一般与进程号相同。
    struct pid *find_vpid(int nr)
    {
    	return find_pid_ns(nr, task_active_pid_ns(current));
    }
    EXPORT_SYMBOL_GPL(find_vpid);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.2 API演示

    我执行top命令,top的pid为44284。

    #include 
    #include 
    #include 
    
    static int __init find_get_pid_init(void)
    {
        struct pid * kpid = find_get_pid(44284); //top的进程号:44284
        //struct pid * kpid=find_get_pid(current->pid); //根据进程号,调用函数获取进程描述符信息
    
        printk("the count of struct pid is :%d\n", kpid->count);    
    
        printk("the level of struct pid is :%d\n", kpid->level);
    
        printk("the upid of struct pid is :%d\n", kpid->numbers[kpid->level].nr);
      
        return -1;
    }
    
    module_init(find_get_pid_init);
    
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    二、get_pid_task

    2.1 API详解

    (1)struct task_struct
    列举了struct task_struct的一些成员,接下来我将通过进程数值ID号,来获取struct task_struct,并打印这些成员。

    struct task_struct {
    	......
    	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
    	......
    	pid_t pid;
    	pid_t tgid;
    	/* process credentials */
    	char comm[TASK_COMM_LEN];  //不包括路径的可执行文件名称 
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中成员 comm 使用 get_task_comm 访问(使用 task_lock() 锁定它),并 由 setup_new_exec 正常初始化 。
    task_lock/task_unlock 用来保护 struct task_struct 的共享资源:比如->fs, ->files, ->mm, ->group_info, ->comm等等。

    // linux-4.10.1/include/linux/sched.h
    
    struct task_struct {
    	......
    	/* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed,
     	* mempolicy */
    	spinlock_t alloc_lock;
    	......
    }
    
    
    /*
     * Protects ->fs, ->files, ->mm, ->group_info, ->comm, keyring
     * subscriptions and synchronises with wait4().  Also used in procfs.  Also
     * pins the final release of task.io_context.  Also protects ->cpuset and
     * ->cgroup.subsys[]. And ->vfork_done.
     *
     * Nests both inside and outside of read_lock(&tasklist_lock).
     * It must not be nested with write_lock_irq(&tasklist_lock),
     * neither inside nor outside.
     */
    static inline void task_lock(struct task_struct *p)
    {
    	spin_lock(&p->alloc_lock);
    }
    
    static inline void task_unlock(struct task_struct *p)
    {
    	spin_unlock(&p->alloc_lock);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    // linux-4.10.1/fs/exec.c
    char *get_task_comm(char *buf, struct task_struct *tsk)
    {
    	/* buf must be at least sizeof(tsk->comm) in size */
    	task_lock(tsk);
    	strncpy(buf, tsk->comm, sizeof(tsk->comm));
    	task_unlock(tsk);
    	return buf;
    }
    EXPORT_SYMBOL_GPL(get_task_comm);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)get_pid_task
    get_pid_task函数通过 struct pid 和 type 来获取进程的struct task_struct结构体。内部实现主要通过pid_task(pid, type)函数来获取struct task_struct结构体。

    struct task_struct *get_pid_task(struct pid *pid, enum pid_type type)
    {
    	struct task_struct *result;
    	rcu_read_lock();
    	result = pid_task(pid, type);
    	if (result)
    		get_task_struct(result);
    	rcu_read_unlock();
    	return result;
    }
    EXPORT_SYMBOL_GPL(get_pid_task);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    get_pid_task函数会将struct task_struct成员的usage变量加1。

    备注:内核中函数带有get/put前缀的函数通常都会将引用计数+1或者-1。

    #define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0)
    
    • 1

    (3)pid_task
    pid_task这个函数是内核中用来获取struct task_struct的常用的辅助函数。用来获取pid->tasks[type]哈希表中的第一个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;
    	
    		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);
    	}
    	return result;
    }
    EXPORT_SYMBOL(pid_task);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    参数struct pid是内核对进程号的内部表示。
    参数type是pid_type型变量,此变量是一个枚举型变量,定义如下:

    enum pid_type
    {
    	PIDTYPE_PID,	//进程的进程号
    	PIDTYPE_PGID,	//进程组领头进程的进程号
    	PIDTYPE_SID,	//会话领头进程的进程号
    	PIDTYPE_MAX		//表示进程号类型的数目 == 3
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.2 API演示

    #include 
    #include 
    #include 
    #include 
    #include 
    
    /* Module parameter */
    static int pid = 0;
    module_param(pid, int, 0);
    
    //内核模块初始化函数
    static int __init lkm_init(void)
    {
        struct pid * kpid;
        struct task_struct *task;
        char task_name[TASK_COMM_LEN] = {0};
    
        kpid = find_get_pid(pid);
        task = get_pid_task(kpid, PIDTYPE_PID);
    
        printk("task name = %s , task pid = %d\n", get_task_comm(task_name, task), task->pid);
    
    	return -1;
    }
    
    module_init(lkm_init);
    
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    [root@localhost get_task_by_pid]# insmod get_task_by_pid.ko pid=1
    [root@localhost get_task_by_pid]# dmesg -c
    [62943.164059] task name = systemd , task pid = 1
    
    • 1
    • 2
    • 3

    三、find_task_by_vpid

    3.1 API详解

    find_task_by_vpid这个函数在内核中根据给定进程 pid 获取进程task_struct,但这个函数没有EXPORT_SYMBOL,因此需要调用kallsyms_lookup_name获取该函数地址后,才能在自己的内核模块中使用。

    struct task_struct *find_task_by_vpid(pid_t vnr)
    {
    	return find_task_by_pid_ns(vnr, task_active_pid_ns(current));
    }
    
    • 1
    • 2
    • 3
    • 4

    (1)find_task_by_pid_ns

    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;
    }
    EXPORT_SYMBOL_GPL(find_pid_ns);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    struct task_struct *pid_task(struct pid *pid, enum pid_type type)
    {
    	struct task_struct *result = NULL;
    	if (pid) {
    		struct hlist_node *first;
    		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);
    	}
    	return result;
    }
    EXPORT_SYMBOL(pid_task);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    /*
     * Must be called under rcu_read_lock().
     */
    struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns)
    {
    	rcu_lockdep_assert(rcu_read_lock_held(),
    			   "find_task_by_pid_ns() needs rcu_read_lock()"
    			   " protection");
    	return pid_task(find_pid_ns(nr, ns), PIDTYPE_PID);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)task_active_pid_ns

    static inline struct pid *task_pid(struct task_struct *task)
    {
    	return task->pids[PIDTYPE_PID].pid;
    }
    
    • 1
    • 2
    • 3
    • 4
    /*
     * ns_of_pid() returns the pid namespace in which the specified pid was
     * allocated.
     *
     * NOTE:
     * 	ns_of_pid() is expected to be called for a process (task) that has
     * 	an attached 'struct pid' (see attach_pid(), detach_pid()) i.e @pid
     * 	is expected to be non-NULL. If @pid is NULL, caller should handle
     * 	the resulting NULL pid-ns.
     */
    static inline struct pid_namespace *ns_of_pid(struct pid *pid)
    {
    	struct pid_namespace *ns = NULL;
    	if (pid)
    		ns = pid->numbers[pid->level].ns;
    	return ns;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
    {
    	return ns_of_pid(task_pid(tsk));
    }
    EXPORT_SYMBOL_GPL(task_active_pid_ns);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.2 API演示

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /* Module parameter */
    static int pid = 1;
    module_param(pid, int, 0);
    
    struct task_struct *(*_find_task_by_vpid)(pid_t vnr);
    
    //内核模块初始化函数
    static int __init lkm_init(void)
    {
        struct task_struct *task;
        char task_name[TASK_COMM_LEN] = {0};
    
        _find_task_by_vpid = (void *)kallsyms_lookup_name("find_task_by_vpid");
        if(!_find_task_by_vpid){
            printk("_find_task_by_vpid is null\n");
            return -1;
        }    
    
    	rcu_read_lock();
    
    	task = _find_task_by_vpid(pid);
    
        if (task){
            get_task_struct(task);
        }
    
    	rcu_read_unlock();
    
        printk("task name = %s , task pid = %d\n", get_task_comm(task_name, task), task->pid);
    
        put_task_struct(task);
    
    	return -1;
    }
    
    module_init(lkm_init);
    
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    # insmod hello.ko pid=2
    # dmesg -c
    [  255.830200] task name = kthreadd , task pid = 2
    
    • 1
    • 2
    • 3

    参考资料

    Linux 3.10.0

  • 相关阅读:
    kuiper安装
    Influence on Social media(素论+思维)
    学习笔记-XXE
    在Git中创建本地分支并关联远程分支
    JavaScript基础标准库总结——(1)
    Android原生项目集成uniMPSDK(Uniapp)遇到的报错总结
    NSSCTF中的pop、babyupload、cve版本签到、奇妙的MD5、easy_html
    异常数据检测 | Python奇异谱分析(SSA)数据缺失值插补
    webpack5零基础入门-6webpack处理图片资源
    给电脑重装系统有什么坏处吗
  • 原文地址:https://blog.csdn.net/weixin_45030965/article/details/126385906