作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
今天群中的LYJ同学遇到一个问题,经过一番讨论研究,我们得到了结果,因此产生了此文。
LYJ写了一个内核模块,功能很简单。但是加载该模块后,通过top命令,发现CPU的负载持续增加,但是CPU的利用率却并不高。为了更突出问题,我只保留了最少的示例代码:
- #include <linux/init.h>
- #include <linux/types.h>
- #include <linux/module.h>
- #include <linux/kthread.h>
- static struct task_struct *sleep_task = NULL;
- static int thread_func(void *data)
- {
- while (1) {
- if (kthread_should_stop()) {
- break;
- }
- ssleep(1);
- }
- return 0;
- }
- static int __init sleep_task_init(void)
- {
- sleep_task = kthread_create(thread_func, NULL, "sleep_task");
- if (IS_ERR(sleep_task)) {
- printk(KERN_ERR "sleep_task: kthread_create failed\n");
- sleep_task = NULL;
- return -1;
- }
- wake_up_process(sleep_task);
- printk(KERN_INFO "sleep_task: init successfully\n");
- return 0;
- }
- static void __exit sleep_task_exit(void)
- {
- if (sleep_task) {
- kthread_stop(sleep_task);
- sleep_task = NULL;
- }
- printk(KERN_INFO "sleep_task: exit successfully\n");
- }
- MODULE_LICENSE("GPL");
- module_init(sleep_task_init);
- module_exit(sleep_task_exit);
在得到上面的代码之前,我们不断的猜测是哪些操作导致了CPU负载的持续增加。然后不断的裁剪代码,最后得到上面的代码后,问题依然存在。
加载模块前:
- top - 10:50:12 up 1:20, 11 users, load average: 0.08, 0.03, 0.01
- top - 10:54:21 up 1:24, 11 users, load average: 0.97, 0.51, 0.20
但是CPU占有率确实仍然非常低。
那么剩下的代码中,最有可能导致这个问题的就是ssleep。查看ssleep的代码:
- static inline void ssleep(unsigned int seconds)
- {
- msleep(seconds * 1000);
- }
- void msleep(unsigned int msecs)
- {
- unsigned long timeout = msecs_to_jiffies(msecs) + 1;
- while (timeout)
- timeout = schedule_timeout_uninterruptible(timeout);
- }
- signed long __sched schedule_timeout_uninterruptible(signed long timeout)
- {
- __set_current_state(TASK_UNINTERRUPTIBLE);
- return schedule_timeout(timeout);
- }
代码如下:
- static int thread_func(void *data)
- {
- while (1) {
- if (kthread_should_stop()) {
- break;
- }
- schedule_timeout_interruptible(HZ);
- }
- return 0;
- }
因为CPU的占有率非常低,所以可以肯定,该线程大部分时间确实不是运行。可是这个结论又与CPU 负载的结果相冲突,并且这个原因还是由于TASK_UNINTERRUPTIBLE造成的。
这样的话,造成这些种种矛盾的原因可能就在于linux的CPU 负载的计算方法。在google搜索了一番,找到了下面的信息:
The system load data is collected by the calc_load( ) function, which is invoked by update_times( ). This activity is therefore performed in the TIMER_BH bottom half. calc_load( ) counts the number of processes in the TASK_RUNNING or TASK_UNINTERRUPTIBLE state and uses this number to update the CPU usage statistics.
上面清楚的说明,Linux在计算CPU负载的时候,TASK_RUNNING和TASK_UNINTERRUPTIBLE两种状态的进程都会被统计。对于本例来说,虽然该thread大部分时间是在sleep,但是其状态不是TASK_RUNNING就是TASK_UNINTERRUPTIBLE,因此CPU的负载会持续增加。
此处留下了一个问题:为什么Linux统计CPU负载的时候,要包括TASK_UNINTERRUPTIBLE呢?今天时间不够了,以后我会继续跟踪这个问题。