Chinaunix

标题: 请问:在内核模块中如何分辨某个进程是处于内核态还是用户态 [打印本页]

作者: vupiggy    时间: 2009-03-12 06:01
标题: 请问:在内核模块中如何分辨某个进程是处于内核态还是用户态
我的理解是可以通过 tasK_struct->thread_struct.ip ,根据其权限位来判断该进程当前运行于何态。望哪位大侠告知:

1. 以上方法可行否?

2. 更正式,不那么 hacky 的方法?(譬如内核提供的函数或宏就可以完成这一任务?)

多谢!
作者: vupiggy    时间: 2009-03-12 08:59
ip应该是不行,权限位是在cs
作者: scutan    时间: 2009-03-12 10:17
CS寄存器中的RPL显示了当前CPU的运行级别.
作者: albcamus    时间: 2009-03-12 11:19
原帖由 scutan 于 2009-3-12 10:17 发表
CS寄存器中的RPL显示了当前CPU的运行级别.


因为是在内核里判断, 所以根本不需要判断当前cpu的runlevel -- 必定是ring0。

楼主估计是要对所有的task都判断一下, 也可能是RUNNING态的task都判断一下。  不管怎样,处于睡眠态的也不需要判断, 因为必定是在内核态睡着了。  只有在RUNNING态、并且没有占据CPU的task, 才需要判断。  这就看这个task是什么时候被抢走了CPU, 保存在其tss_struct中的寄存器是怎样的。
作者: scutan    时间: 2009-03-12 12:50
原帖由 albcamus 于 2009-3-12 11:19 发表


因为是在内核里判断, 所以根本不需要判断当前cpu的runlevel -- 必定是ring0。

楼主估计是要对所有的task都判断一下, 也可能是RUNNING态的task都判断一下。  不管怎样,处于睡眠态的也不需要判断, 因为 ...


嗯. 我搞错了.

我觉得应该是看进程的thread_struct结构中的内容吧. 因为那个tss_struct是针对CPU的, 不是针对某个进程的.
说得不对还请指教.
作者: albcamus    时间: 2009-03-12 13:03
原帖由 scutan 于 2009-3-12 12:50 发表


我觉得应该是看进程的thread_struct结构中的内容吧. 因为那个tss_struct是针对CPU的, 不是针对某个进程的.
说得不对还请指教.



对对, 我说的有问题。 一个进程被换走的时候, 当前tss_struct里的东西就是保存在其thread_struct里的
作者: xpl    时间: 2009-03-12 13:29
以前用过这样一个方法,你可以参考一下:
定义一个局部变量,通过这个局部变量的位置来判断是在内核态还是用户态
作者: scutan    时间: 2009-03-12 14:13
刚刚想了一下.
对于一个处于RUNNING状态的进程, 如果此时它没有占用CPU, 则在上次发生进程切换时, 它可能运行于用户态或内核态.
假设在发生进程切换时它运行于内核态, 那它可能是通过系统调用或者是异常(如缺页异常)的方式进入到内核态的.
又假设在发生进程切换时它运行于用户态, 那它只能是通过中断进入到内核态. 进而被切换掉.
而通过中断 系统调用或异常这三种方式进入到内核态时, 有一个不同之处就在于在压栈时, 内核栈中的出错代码那个地方.
如果是由于中断进入到内核, 那么在该进程的内核栈的EIP指针下面压入栈的值应该是 (中断号-256)
而如果是由于异常进入到内核的, 那么在该位置处, 压入栈的值应该是异常的出错代码, 并且在压栈时会将其再设置为-1; 如果是由于系统调用进入到内核的, 那么在该位置处, 其值为系统调用号.

在情景分析第235页的那个图对这个有讲解, 楼主看看能否满足你的需求?
作者: xiegang112    时间: 2009-03-12 14:24
标题: 回复 #8 scutan 的帖子
为什么不能用进程保存的ip地址来判断呢?
作者: scutan    时间: 2009-03-12 14:42
原帖由 xiegang112 于 2009-3-12 14:24 发表
为什么不能用进程保存的ip地址来判断呢?


因为进程在切换出去的时候, 是处于内核态的. 此时的IP也是内核态的. 用户态的IP在进程切换到内核态时保存到了内核栈中.
作者: 思一克    时间: 2009-03-12 16:15
address = thread_struct ->cr2
if(address >= TASK_SIZE)
   in kernel
else
   in user

对于正在其它CPU上跑的, 如何判断?
作者: scutan    时间: 2009-03-12 16:26
原帖由 思一克 于 2009-3-12 16:15 发表
address = thread_struct ->cr2
if(address >= TASK_SIZE)
   in kernel
else
   in user

对于正在其它CPU上跑的, 如何判断?


CR2寄存器中在发生页异常的时候存储页故障地址的, 那怎样识别系统调用的情况呢?
至于你说的正在其它CPU上跑的进程, 我觉得可以获得那个CPU上的CS寄存器来查看.
呵呵, 说得不对还请指教.
作者: wenxy    时间: 2009-03-12 16:39
Mark.
作者: 思一克    时间: 2009-03-12 16:43
你说的对. 我错的.
thread_struct->eip 可以?

原帖由 scutan 于 2009-3-12 16:26 发表


CR2寄存器中在发生页异常的时候存储页故障地址的, 那怎样识别系统调用的情况呢?
至于你说的正在其它CPU上跑的进程, 我觉得可以获得那个CPU上的CS寄存器来查看.
呵呵, 说得不对还请指教.

作者: scutan    时间: 2009-03-12 16:47
原帖由 思一克 于 2009-3-12 16:43 发表
你说的对. 我错的.
thread_struct->eip 可以?



我觉得 EIP也不行. 因为一个进程在发生切换的时候, 都已经被中断了, 处于内核态了.
我刚才测试了一下. 对一个进程只运行
while (1) ;
然后从内核模块去查看, 这个进程的EIP仍然是大于3G的.
作者: wenxy    时间: 2009-03-12 17:11
0xc0000000 之上是内核空间。
作者: xiegang112    时间: 2009-03-12 19:11
标题: 回复 #15 scutan 的帖子
我有点不明白楼主的所说的“分辨某个进程处于内核态”中的某个进程是指的任一进程吗?
如果是这样,在单cpu情况下,应该是某一时刻应该只有一个进程处于用户态。其他进程应该都在内核态吧。因为其他进程,要么是调用系统调用阻塞或时间片到被切换出去,要么是中断发生,被抢占或者时间片到被切换出去。不论哪种情况,进程都在内核太呀。
作者: scutan    时间: 2009-03-12 19:19
原帖由 xiegang112 于 2009-3-12 19:11 发表
我有点不明白楼主的所说的“分辨某个进程处于内核态”中的某个进程是指的任一进程吗?
如果是这样,在单cpu情况下,应该是某一时刻应该只有一个进程处于用户态。其他进程应该都在内核态吧。因为其他进程,要么 ...


应该是指任一进程.

嗯. 是的, 进程在发生切换时都处于内核态.

前面albcamus也提到了, 如果不是RUNNING状态, 那么那个进程肯定是处于内核态的. 而如果该进程是RUNNING状态. 则该进程处于运行队列中. 但是有可能这个进程并没有占用CPU, 只是在那个运行队列中而已.

所以我的理解是, 楼主是不是想知道, 这个处于运行队列中的进程, 在上次被切换出去的时候, 是从哪个状态被切换出去的? 如果是用户态, 那就可能是先被中断了, 再切换出去. 如果是内核态, 那就可能是该进程先执行了系统调用, 在内核态运行时由于时间片到了, 再被切换出去的.
作者: xiegang112    时间: 2009-03-12 19:27
标题: 回复 #18 scutan 的帖子
正解,我就是这么理解的。是不是楼主需要知道在被中断时(系统调用是用户主动进入内核态,所以不算),进程是否处于用户态。
作者: scutan    时间: 2009-03-12 21:12
原帖由 xiegang112 于 2009-3-12 19:27 发表
正解,我就是这么理解的。是不是楼主需要知道在被中断时(系统调用是用户主动进入内核态,所以不算),进程是否处于用户态。


嗯. 所以我觉得可以通过查看某个进程的内核栈, 看内核栈上的相应位置保存的值是系统调用号还是(中断号-256).
作者: vupiggy    时间: 2009-03-13 02:32
原帖由 scutan 于 2009-3-12 12:19 发表
所以我的理解是, 楼主是不是想知道, 这个处于运行队列中的进程, 在上次被切换出去的时候, 是从哪个状态被切换出去的? 如果是用户态, 那就可能是先被中断了, 再切换出去. 如果是内核态, 那就可能是该进程先执行了系统调用, 在内核态运行时由于时间片到了, 再被切换出去的.


大致是这个意思,但未必是指``上次''被切换出去的进程,也可以是正在运行于某个cpu上的进程(smp 系统),也就是所有处于 RUNNING 状态的进程,无论当前是否在 CPU 上,都需要知道它``当前'' (如果被切换,就是被切换前) 处于内核态还是用户态。

EIP 看来是不可行了。

albcamus 的一个建议:``当前tss_struct里的东西就是保存在其thread_struct里的''感觉可行,不过我没找到在 thread_struct 的哪个成员反映了这个进程被切换前所处的是内核态还是用户态。

scutan 的建议:``查看某个进程的内核栈, 看内核栈上的相应位置保存的值是系统调用号还是(中断号-256)'' 这个比较直观,直接可以得到内核栈的地址,问题是然后呢? ... 内核栈的什么位置是存放系统调用号或是中断号的?如果不总是在固定位置,或者这个位置因内核版本是会改动的,那么这个方案还是有点 hackish,是吧?

请大家继续支招,多谢多谢!
作者: springtty    时间: 2009-03-13 15:01
mark,学习
作者: tianyizhou    时间: 2010-03-18 22:50
这是在内核2.6.16.2看到的代码,不知道有没有帮助
144ret_from_intr:
145        GET_THREAD_INFO(%ebp)
146        movl EFLAGS(%esp), %eax         # mix EFLAGS and CS
147        movb CS(%esp), %al
148        testl $(VM_MASK | 3), %eax
149        jz resume_kernel
作者: zx_wing    时间: 2010-03-18 23:23
帖子太长了,也没细看大家说的。
如果我来做的话,想到的一个比较简单的是看这个进程的esp指向的是内核栈还是用户栈。指向内核栈就在内核中,反之亦然。
当然有一个问题就是在多核情况下,你是无法判定准的,因为很可能你判定的时刻它是在内核态的,但当你要做什么操作时,它已经回到用户态了
作者: re_load    时间: 2010-03-19 08:26
问题是,(在SMP中)所有的进程状态都是在不断变化的,你这个遍历过程中,系统状态已经发生了很多变化。记录系统某一时刻状态的意义是什么呢?观察观察还可以,没什么附加操作可以做




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2