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

在《 Linux如何终止D状态的进程》,我浮光掠影般描述了一种非规范的结束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进程将被杀死!
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值