-
原文地址 http://www.blogbus.com/wanderer-zjhit-logs/183074985.html 这个博客的作者写的文章不错,可以常去看看。
-
0 内核全局符号只有使用了EXPORT_SYMBOL导出的才能被模块通过内核头文件引用;否则会提示:undefined symbol,此时只能通过查看地址强转的方式引用,但是失去了可移植性;如果某些全局变量使用的是EXPORT_SYMBOL_GPL导出的,必须在内核模块中用MODULE_LICENSE("GPL");申明方可使用,否则会出现提示:unsigned symbol find_vpid(err 0)错误提示
1 多个线程间的同步问题
问题1.1插入模块后,内核子线程会执行周期性的调度,直到内核卸载,但是卸载时,子内核线程可能一般处于睡眠状态,此时如果模块卸载,即模块占用的内核系统资源会被释放,如果此时子线程被调度执行,会出现错误,用dmesg -c查看如下所示:
kernel_thread_helper->testThread->__wake_up-->__wake_up_common,EIP = NULL
解决办法:
卸载模块时,必须首先唤醒子线程,让子线程先退出,然后进行卸载过程<也可以调用daemonize()函数使子线程成为daemon,但是那样只能在系统关机时关闭子线程,不符合要求>
问题1.2 两个子线程细粒度的同步
最简单方法,是等待线程调用msleep睡眠若干个时钟周期,不同系统上的jiffies周期长度不一样,大约10ms,显然粒度不够;另一种是父线程通过sleep_on_timeout来睡眠等待,类似于用户态进程间同步的信号量实现方法,粒度可以达到us级别;最高级别的是直接在父线程中通过schedule()来实现实时切换,以等待子线程限制性,类似于锁的实现机制,实时级别更高
2 解决内核子线程defunct问题
关于子进程僵死的问题网页上一大堆,都是将用户层子线程的处理办法,都是采用信号处理机制实现,这种机制实现于线程从内核态返回到用户态的过程中,通过系统时钟来确保其实现,内核模块永远在内核中运行,且我的内核模块中由于父线程是个周期性线程,为了效率也不能及时回收每个已结束子线程的task_struct资源。从而,模块运行一段时间,用ps aux可以查看到大量的defunct状态的子线程,由于内核中的进程数是有限制的,这样会降低系统的效率。内核defunct子线程如何及时清除呢?有两种方法:
2.1 子线程主动与父线程脱离关系
这种方式非常好,类似于用户态线程的pthread_detach函数,将子线程与init线程建立关系,从而当子线程结束时,让init守护线程为其收尸,方法如下:
subProcess->parent = subProcess->real_parent = init_pid_ns.child_reaper
list_del(&subProcess->sibling);
list_add(&subProcess->sibling,&subProcess->parent->children);
2.2 父线程被动结束父子关系:
此时又有两种方法:
2.2.1 同步等待子线程结束,并回收其资源
当子线程通过return函数结束自身运行时,task->state:TASK_DEAD,task->exit_state:EXIT_ZOMBIO
按道理,子线程退出时应该像父线程发送SIGCHLD信号,但是经过测试,内核中子线程退出时,并未向父线程发送SIGCHLD信号;测试如下:
子线程执行程序:
static int loop(void* ptr)
{
printk("in loop\n");
(*((do_notify_parentt)(0xc105a1f0)))(current,SIGCHLD);
//return 0;
// (*((sys_exitt)(0xc104bd60)))(0); 这三个都测过}
父线程:
int force_init(void)
{struct sigpending* sigpending = ¤t->signal->shared_pending; //sys_exit发送给该变量
sigset_t * set = &sigpending->signal;
sigset_t* set2 = ¤t->pending;
...
if(set2->sig[sig/__NSIG_BPW] & 1 <<(sig % __NSIG_BPW))
printk("match2,%lu\n",set2->sig[0]);
if(set->sig[sig/__NSIG_BPW] & 1 <<(sig % __NSIG_BPW))
printk("match,%lu\n",set->sig[0]);}
此时父线程主要通过循环检测子线程状态,当发现子线程处于EXIT_ZOMBIL状态时,利用未导出函数实现回收子线程资源:(*((release_taskk)(0xc104a4b0)))(sub);
结果父线程中没有显示信息2.2.2 父线程通过采用2.1类似方法断绝父子关系:
父线程循环执行下面函数:
struct list_head* list;
list_for_each(list,¤t->children){
task = list_entry(list,struct task_struct,sibling);
if(task->pid == subPid)
break;
}
subProcess->parent = subProcess->real_parent = init_pid_ns.child_reaper
list_del(&subProcess->sibling);
list_add(&subProcess->sibling,&subProcess->parent->children);面临困惑:在proc文件下利用proc_mkdir,和proc_create_entry新建了大量的目录和文件,但是,在图形界面有一个目录无法打开,一打开就死机;