我在内核模块中有以下代码,它在进程树的基础上打印进程名称和uid直到init进程:
// recursivly walk the task's parent until we reach init
void parent_task_walk(struct task_struct *task) {
struct task_struct *parent;
char filename[MAX_FILE_LEN];
if (task && task->mm) {
parent = get_task_parent(task);
printk("%s (uid:%d)", exe_from_mm(task->mm, filename, MAX_FILE_LEN),
get_task_uid(task));
if (parent && task->pid != 1) {
printk(", ");
parent_task_walk(parent);
}
}
}
注意:我使用了一些引用真实内核函数的宏,因为这是针对内核模块的多个版本。源代码位于此文件中:https://github.com/cormander/tpe-lkm/blob/319e1e29ea23055cca1c0a3bce3c865def14d3d2/core.c#L61
输出最终看起来像这样:
/bin/bash (uid:500), /usr/sbin/sshd (uid:500), /usr/sbin/sshd (uid:0), /usr/sbin/sshd (uid:0), /sbin/init (uid:0)
这是一个递归函数。你可以想象,当你启动200个bash shell然后触发事件时,事情会变得很糟糕。我不确定究竟发生了什么,但机器冻结了。内核耗尽了我假设的堆栈空间,去了OOM,然后自己拍摄了吗?
我想知道处理这种情况的最佳方法是什么。我看到了几个选项:
1)在N个进程
之后停止遍历进程树2)在一些字符数组(最终将被打印)已满后停止行走
3)使用goto
而不是递归函数,但仍然遵守选项#1和#2
4)使用一些你将为我阐述的其他非递归方法
这发生在内核空间,所以不是最好客的环境。任何人都可以指出最好的方法吗?
答案 0 :(得分:5)
事实上,您可能会因为使用递归函数而发生内核崩溃。
你可以使用一个非常简单的循环而不是你的递归函数......你是来自一个只有函数编程的世界吗? while循环在C中是标准的,但如果你真的想使用goto,你可以......;)这是使用循环的代码。
void parent_task_walk(struct task_struct* task)
{
struct task_struct *parent = NULL;
char filename[MAX_FILE_LEN];
while (task && task->mm) {
parent = get_task_parent(task);
printk("%s (uid:%d)",
exe_from_mm(task->mm, filename, MAX_FILE_LEN),
get_task_uid(task));
if (parent && task->pid != 1)
printk(", ");
task = parent;
}
}