红联Linux门户
Linux帮助

Linux x86内核终止D状态的进程

发布时间:2016-12-14 09:55:42来源:dog250作者:Bomb250
在《Linux如何终止D状态的进程》(http://www.linuxdiyf.com/linux/26882.html),我浮光掠影般描述了一种非规范的结束D进程的方法,只是一种方法,几乎没有可操作性。今天收到了一位朋友的邮件,问我exit_task1到底该怎么写。说实话,我本来不想把故事说完的,但是既然有人问了,我就再写两篇短文吧。本文介绍x86 32位系统中的详细方法,下一篇文章将介绍64位系统的方法。
 
我的exit_task1是以下这么写的:
void exit_task1()  
{  
// 这个有点猛,仅为测试。  
// emergency_restart();  
// TODO:这里要做的事情非常多,类似ret_from_fork那样,要执行schedule_tail后处理之类的事情。  
// 首先,你必须preempt_enable_no_resched,不然会锁死,其次,还要考虑schedule_tail的逻辑。  
}
 
事实上,以上写的对于大多数想拿来代码就编译运行的复制粘贴者而言,没有任何用处。到底该怎么办呢?我说过,要照着ret_from_fork做:
ENTRY(ret_from_fork)  
CFI_STARTPROC  
pushl %eax  
CFI_ADJUST_CFA_OFFSET 4  
call schedule_tail  
GET_THREAD_INFO(%ebp)  
popl %eax  
CFI_ADJUST_CFA_OFFSET -4  
pushl $0x0202   # Reset kernel eflags  
CFI_ADJUST_CFA_OFFSET 4  
popfl  
CFI_ADJUST_CFA_OFFSET -4  
jmp syscall_exit  
CFI_ENDPROC  
END(ret_from_fork)
 
那么我也写个汇编文件func_.S:
#include <linux/linkage.h>  
#include <asm/thread_info.h>
.global addr;  
addr:  
.fill 1,4,0 
ENTRY(ret_from_disksleep)  
pushl %eax  
//  call schedule_tail   // schedule_tail没有导出,因此我只能从kallsyms里面找到0xc045d420这个地址。  
call 0xc045d420 // 直接call函数地址  
GET_THREAD_INFO(%ebp)  
popl %eax  
pushl $0x0202   # Reset kernel eflags  
popfl  
call do_exit // 调用exit,退出D进程  
END(ret_from_disksleep)
 
完整的rmpid.c文件如下:
#include <linux/module.h>  
#include <linux/sched.h>  
#include <linux/errno.h>  
#include <linux/sched.h>  
#include <linux/kernel.h>
static int pid __read_mostly = 0;  
module_param(pid, int, 0644);  
MODULE_PARM_DESC(pid, "pid");
static int address __read_mostly = 0;  
module_param(address, int, 0644);  
MODULE_PARM_DESC(address, "type");
extern unsigned long addr;
asmlinkage void ret_from_disksleep(void) __asm__("ret_from_disksleep");
static int __init mymm_register(void)  
{  
struct task_struct *p;  
// TMD,在2.6.32中,kallsyms_lookup_name本身没有被导出!!  
//addr = kallsyms_lookup_name("schedule_tail");  
addr = address;  
if (pid > 0) {  
for_each_process(p) {  
if (task_pid_vnr(p) == pid) {  
set_task_state(p, TASK_INTERRUPTIBLE);  
p->thread.ip = (unsigned long)ret_from_disksleep;  
wake_up_process(p);  
break;  
}  
}  
}  
out:  
return -ENOMEM;  
}  
module_init(mymm_register);  
MODULE_AUTHOR("aaaa");  
MODULE_LICENSE("GPL");  
MODULE_DESCRIPTION("pid remove");
 
然后Makefile中把func_.S链进去即可:
obj-m += pidrm.o
pidrm-objs += rmpid.o func_.o
obj-m += bug4.o
 
编译完毕就可以演示了!在Makefile中,我顺带编译了一个bug4模块,它是一个有bug的模块,为了制造D进程:
#include <linux/module.h>  
#include <linux/sched.h>
static int __init bug4_register(void)  
{  
struct  module *o = THIS_MODULE;  
// 递增一个永远都不会减的引用计数!  
try_module_get(o);  
return 0;  
}
static void __exit  
my_cleanup_module(void)  
{  
}  
module_init(bug4_register);  
module_exit(my_cleanup_module);  
MODULE_AUTHOR("aaaa");  
MODULE_LICENSE("GPL");  
MODULE_DESCRIPTION("bug");
 
一切编译工作都完成后,我们试着加载bug4.ko,然后运行:
rmmod --wait bug4
此时rmmod就变成D进程了!当我们以rmmod进程的pid为参数载入pidrm模块的时候,该rmmod进程将被杀死!
 
本文永久更新地址:http://www.linuxdiyf.com/linux/26883.html