有时候我们想关注某一个任务的cpu利用率,除了使用top还有什么其他方法可行呢?
实际上cpu利用率的原理就是特定时间内一个任务总的运行时间与时间的比值即可简单的认为是cpu利用率。
那么就有两个问题:1)就是任务在某个时间周期的运行时间;2) 周期性的计算cpu利用率。
第一个问题任务在某个时钟周期的运行时间可以通过当前时间任务总的运行时间p->se.sum_exec_runtime减去上一个统计周期任务的运行时间,就可得到此统计周期内任务的运行时间;第二个问题cpu利用率就可用第一步计算的运行除以周期就得到cpu利用率,而这个周期可以通过内核的htimer定时器来实现。
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <asm/io.h>
#include <linux/cpu.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/pid_namespace.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/delay.h>
#define MAX_INT_STRING_NUM 20
static u64 __read_mostly sample_period = 4 * ((u64)NSEC_PER_SEC); /* period=4s */
static struct hrtimer watchcat_timer;
static pid_t taskid ;
static struct task_struct *tsk;
u64 last_runtime = 0, difftime = 0;
static struct task_struct *find_task_by_vpid2(pid_t vnr)
{
return pid_task(find_pid_ns(vnr, task_active_pid_ns(current)), PIDTYPE_PID);
}
static int monitask_read_proc(struct seq_file *m, void *v)
{
//rcu_read_lock();
tsk = find_task_by_vpid2(taskid);
if (tsk) {
get_task_struct(tsk);
difftime = tsk->se.sum_exec_runtime - last_runtime;
last_runtime = tsk->se.sum_exec_runtime;
seq_printf(m, " cpu rate of %s:%d is %lld.%d \n", tsk->comm, tsk->pid, difftime/(4*1000*1000*10), (int)(difftime%(4*1000*1000*100)));
put_task_struct(tsk);
goto out;
}
//rcu_read_unlock();
out:
return 0;
}
static int monitask_open(struct inode *inode, struct file *file)
{
return single_open(file, monitask_read_proc, NULL);
}
static ssize_t monitask_write_proc(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
char kbuf[MAX_INT_STRING_NUM + 1] = {0};
if (count <= 0 || count > MAX_INT_STRING_NUM)
return -EINVAL;
if (copy_from_user(&kbuf, buffer, count))
return -EFAULT;
if (kstrtoint(kbuf, 0, &taskid) || taskid < 0)
return -EINVAL;
//rcu_read_lock();
if (taskid) {
tsk = find_task_by_vpid2(taskid);
if (!tsk) {
//rcu_read_unlock();
return -ESRCH;
}
}
last_runtime = tsk->se.sum_exec_runtime;
//rcu_read_unlock();
hrtimer_start(&watchcat_timer, ns_to_ktime(sample_period),
HRTIMER_MODE_REL_PINNED);
pr_info(" start timer fun:last_runtime=0x%llx\n", last_runtime);
return count;
}
static const struct file_operations monitask_fops = {
.open = monitask_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = monitask_write_proc,
};
static enum hrtimer_restart watchcat_timer_fn(struct hrtimer *hrtimer)
{
/*
* todo:
* smp should protect tsk
*/
//rcu_read_lock();
tsk = find_task_by_vpid2(taskid);
if (tsk) {
difftime = tsk->se.sum_exec_runtime - last_runtime;
last_runtime = tsk->se.sum_exec_runtime;
}
//rcu_read_unlock();
//pr_info(" cpu rate of %s:%d is %lld%\n", tsk->comm, tsk->pid, difftime/(4*1000*1000)/1000);
hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
return HRTIMER_RESTART;
}
static void init_watchcat_func(struct work_struct *dummy)
{
hrtimer_init(&watchcat_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
watchcat_timer.function = watchcat_timer_fn;
watchcat_timer.irqsafe = 1;
}
static int __init monitask_init(void)
{
struct proc_dir_entry *entry;
entry = proc_create("monitask", S_IWUSR | S_IRUSR, NULL, &monitask_fops);
if (entry == NULL)
{
printk(KERN_ERR "Failed to register /proc/monitask\n");
remove_proc_entry("monitask", NULL);
return -ENOMEM;
}
init_watchcat_func(NULL);
return 0;
}
static void __exit monitask_exit(void)
{
hrtimer_cancel(&watchcat_timer);
remove_proc_entry("monitask", NULL);
}
module_init(monitask_init);
module_exit(monitask_exit);
MODULE_LICENSE("GPL");
上面的代码有一个竞争问题:通过pid获取到task_struct后,有可能这个task会退出,这就有可能导致取task的成员时发生控制在访问的情况。如何解决这个问题还待进一步探索。