linux kernel中如何得到当前的进程信息
我这里内核版本是2.6.35,cpu架构是x86_32.先来看linux下进程的结构。
首先我们要知道在linux中第一个进程是内核进程,pid为0,它是所有的进程的父进程。这个进程也叫swapper,或者说是idle.
这个进程是静态初始化的,定义是在init_task.c中,如下:
[code lang=”c”]
union thread_union init_thread_union __init_task_data =
{ INIT_THREAD_INFO(init_task) };
#define init_task_data __attribute((section(“.data..init_task”)))
[/code]
这里要注意的就是__init_task_data这个宏,这个宏就是将init_thread_union放到.data..init_task这个段里面。还有就是这个是一个thread_union的联合体,我们后面会介绍这个。
这个进程主要用途就是保证至少会有一个进程还在运行,也就是说当没有进程运行的情况下,它是最后的那个进程,就是swaaper 进程.init进程就是由它派生出来的。
然后来看对应的初始化,在header32.s中,这里是将init_thread_union的地址加上THREAD_SIZE(栈的大小)装载进esp,然后__BOOT_DS装载进ss。32为x86默认栈的大小为8k,因此一开始esp的值就是init_thread_union的地址+栈的大小。
1 |
|
我们知道在linux内核中,进程被分为两部分,一部分是thread_info,保存在内核栈中,为了保持很小,因此只保存了必须的几个域,它有一个变量task,指向task_struct,这个结构保存了进程相关的所有信息。而对应的task_struct也保存了一个变量stack指向的就是一个thread_union的联合体,先来看这个联合体的结构:
1 |
|
可以看到它其中一部分是thread_info,一部分是stack,其实这个很好理解,那就是表示它会分配THREAD_SIZE大小的内存然后前面一部分,也就是低地址部分保存thread_info,而高地址部分(剩余部分)存放内核栈.这里我们要知道,所有的进程是共享一个内核栈的(x86_64位的smpj架构下会有多个内核栈,此时一个cpu上的进程共享本cpu上的内核栈,不过具体为什么x86_64下面需要多个kernel stack还不清楚.),而内核栈的thread_info部分也就是保存了当前正在运行的进程(smp下面也就是当前cpu正在运行的进程),内核使用current能够直接存取当前正在运行的任务还有current_thread_info来直接取得当前的thread_info,接下来我们就来看这2个函数的实现。
首先是smp下面kernel stack和current_task的定义
1 |
|
可以看到kernel stack和current_task都是为每个cpu定义了一个,这样的话,读取这两个我们之需要简单的使用percpu_read_stable就可以了,来看current的实现。
1 |
|
然后是current_thread_inf用来直接取得当前的cpu上的thread_info的地址,这里它的计算是这样的,把esp的最后n(THREAD_SIZE栈大小的位数)位清空,那么就是基地址了,也就是thread_info的值.
1 |
|
通过上面的分析,现在我们得到当前进程信息就很容易了,通过$esp来清除后13位的值,得到thread_info,然后通过thread_info自然很容易得到current_task了。
下面就是通过gdb来验证我们的方法。
(gdb) b start_kernel
(gdb) c
//打印当前的task的stack,也就是thread_info地址
(gdb) p current_task->stack
$35 = (void *) 0xc1698000
//打印esp的值
(gdb) p $esp
$36 = (void *) 0xc1699fe4
(gdb) p current_task
$38 = (struct task_struct *) 0xc169f460
//将$esp的值后13位清空,然后就是当前的进程的thread_info的值,然后打印出它的comm,进程名,可以看到就是0号进程。
(gdb) p ((struct thread_info *)0xc1698000)->task->comm
$41 = “swapper\000\000\000\000\000\000\000\000”
//通过下面可以看到地址和current_task相同。
(gdb) p ((struct thread_info *)0xc1698000)->task
$40 = (struct task_struct *) 0xc169f460