全部博文(71)
分类: LINUX
2009-11-07 10:02:35
内核工具
内核线程
内核线程是在内核里面实现后台任务的一种方法。任务能处理异步事件或睡眠等待一个事件的发生。有点类似与用户进程,除了它存活在内核空间并且可以访问内核函数和数据结果外。和用户进程相同,由于抢占调度内核线程也可以独占处理器。一些设备驱动定制内核线程服务来实现助手或帮助任务。例如 USB驱动核心的khubd内核线程监控USB hubs和配置热插拔的USB设备。
创建内核线程
假设你要内核异步触发一个用户模式程序来发送你的email或当它察觉到内核的关键数据结构恶化时产生分页告警。(例如网络接受缓冲的空闲空间降到水平线以下)。
通过内核线程实现基于以下原因:
1、它是后台任务因为它要等待异步事件。
2、需要访问内核数据结构因为实际的事件检测是由一部分内核来做的。
3、要引发一个用户模式程序,这个是个很耗时的操作。
使用kernel_thread()来创建一个内核线程:
ret = kernel_thread(mykthread, NULL, CLONE_FS|CLONE_FILES|CLONE|SIGHAND|SIGCHILD);
标志指明父子线程之间共享资源。CLONE_FILES表明打开文件共享,CLONE_SIGHAND表明信号处理函数共享。
线程由daemonize()启动,它执行初始化并改变线程的父亲为一个名为kthreadd的内核线程。因为daemonize()默认阻塞所有信号,如果线程想处理一个特定信号就使用allow_signal()来使能传递。内核中没有任何信号处理函数,使用signal_pending()来检查信号并采取适当的处理。
kernel_thread()快废弃了,改由高级别的kthread API,它是基于前者构建的。
下面代码请求SIGKILL信号传递并在接受到信号后灭亡。
static DECLARE_WAIT_QUEUE_HEAD(myevent_waitqueue);
rwlock_t myevent_lock;
extern unsigned int myevent_id;
static int mykthread(void *unused)
{
unsigned int event_id = 0;
DECLARE_WAITQUEUE(wait, current);
/*不附加用户资源成为内核线程*/
daemonize("mykthread");
allow_signals(SIGKILL);
/*线程在等待队列上睡眠直到被负责察觉感兴趣的数据结构的内核部分唤醒*/
add_wait_queue(&myevent_waitqueue, &wait);
for(;;) {
/*放弃处理器直到事件发生*/
set_current_state(TASK_INTERRUPTIBLE);
schedule(); /*运行内核其他部分运行*/
if(signal_pending(current)) break;
/*当线程被唤醒,得到CPU控制权*/
read_lock(&myevent_lock); /*临界区开始*/
if (myevent_id) {
event_id = myevent_id;
read_unlock(&myevent_lock);
/*引发注册的用户模式帮助器并传递识别码到它的执行环境*/
run_umode_handler(event_id);
} else {
read_unlock(&myevent_lock);
}
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&myevent_waitqueue, &wait);
return 0;
}
用户模式帮助器:
static void run_umode_handler(int event_id)
{
int i = 0;
char *argv[2], *envp[4], *buffer=NULL;
int value;
argv[i++] = myevent_handler;/*定义在kernel/sysctl.c*/
if (!(buffer = kmalloc(32, GFP_KERNEL))) return;
sprintf(buffer, "TROUBLED_DS = %d", event_id);
if (!argv[0]) return;
argv[i] = 0;
i = 0;
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/usr/sbin:/usr/bin";
envp[i++] = buffer;
envp[i] = 0;
value = call_usermodehelper(argv[0], argv, envp, 0);
kfree(buffer);
}
你必须通过/proc/sys目录中的一个结点注册run_umode_handler()触发的用户模式程序。确信CONFIG_SYSCTL在内核配置中是使能并且添加一个entry到kernel/sysctl.c中的kern_table数组:
{
.ctl_name = KERN_MYEVENT_HANDLER,
.procname = "myevent_handler",
.data = &myevent_handler,
.maxlen = 256,
.mode = 0644,
.proc_handler = &proc_dostring,
.strategy = &sysctl_string,
},
这样就在进程文件系统中创建了结点/proc/sys/kernel/myevent_handler。注册用户模式帮助其,按照下面来做:
bash>echo /path/to/helper>/proc/sys/kernel/myevent_handler
这会在run_umode_handler()触发时执行/path/to/helper。helper可以是一个简单的类似下面的发送包含从系统中收集到的信息的邮件提醒脚本:
bash>cat /path/to/helper
#!/bin/bash
echo kernel datastructure $TROUBLED_DS is in trouble | mail -s Alert root