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);

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

static inline struct pid *get_pid(struct pid *pid)
{
	if (pid)
		atomic_inc(&pid->count);
	return pid;
}

(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 API演示

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

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>

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");

二、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];  //不包括路径的可执行文件名称 
	......
}

其中成员 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);

// 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);

(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);

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

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

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

(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);

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

enum pid_type
{
	PIDTYPE_PID,	//进程的进程号
	PIDTYPE_PGID,	//进程组领头进程的进程号
	PIDTYPE_SID,	//会话领头进程的进程号
	PIDTYPE_MAX		//表示进程号类型的数目 == 3
};

2.2 API演示

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>

/* 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");
[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

三、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)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);
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);
/*
 * 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);
}

(2)task_active_pid_ns

static inline struct pid *task_pid(struct task_struct *task)
{
	return task->pids[PIDTYPE_PID].pid;
}
/*
 * 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;
}
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);

3.2 API演示

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/kallsyms.h>

/* 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");
# insmod hello.ko pid=2
# dmesg -c
[  255.830200] task name = kthreadd , task pid = 2

参考资料

Linux 3.10.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值