内核栈溢出

有一个内核模块和一个用户态程序, 用户态程序通过ioctl调用模块中的接口。 内核模块中有一个switch case结构来区分用户态的请求以确定调用相应接口。 这个switch case比较大,大概有100多个case分支。目前遇到问题:用户态程序调用接口时会使内核崩溃, 提示是内核地址错误什么的。 目前已做过的调试方法:怀疑case过多, 去掉一些case分支, 用户态程序短时间(比如半个小时)重复调用某些接口时正常, 但长时间 (比如2小时以上) 重复调用也会出现内核崩溃。 (注:模块中的接口应该是没有问题的,运行过程中内存消耗正常。 程序运行环境是嵌入式设备)大家有没有遇到过这种情况? case分支过多会不会引发这种情况,还是只是假象? 对linux内核编程不熟悉, 请大家帮帮忙, 谢谢!没有内存释放的问题,起码我写的代码里面都没有涉及内存申请的。是内核崩溃了,不是程序崩溃了, 应该怎么调试?
2026年09月28日 00:31
有3个网友回答
网友(1):

你没有释放内存 最终造成内存出错

网友(2):

报地址错误应该与指针使用不正常有关系
一是使用野指针
二是重复释放已释放过的指针
三是数据溢出、越界(尤其检查字符数组,即字符串数据的变化)

加一些调试语句,或采用-g编译 通过gdb等方法 来观察、跟踪一下运行程序吧。

网友(3):

您好,很高兴为您解答。
1.进程的堆栈

内核在创建进程的时候,在创建task_struct的同事,会为进程创建相应的堆栈。每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存在于内核空间。当进程在用户空间运行时,cpu堆栈指针寄存器里面的内容是用户堆栈地址,使用用户栈;当进程在内核空间时,cpu堆栈指针寄存器里面的内容是内核栈空间地址,使用内核栈。
2.进程用户栈和内核栈的切换

当进程因为中断或者系统调用而陷入内核态之行时,进程所使用的堆栈也要从用户栈转到内核栈。

进程陷入内核态后,先把用户态堆栈的地址保存在内核栈之中,然后设置堆栈指针寄存器的内容为内核栈的地址,这样就完成了用户栈向内核栈的转换;当进程从内核态恢复到用户态之行时,在内核态之行的最后将保存在内核栈里面的用户栈的地址恢复到堆栈指针寄存器即可。这样就实现了内核栈和用户栈的互转。

那么,我们知道从内核转到用户态时用户栈的地址是在陷入内核的时候保存在内核栈里面的,但是在陷入内核的时候,我们是如何知道内核栈的地址的呢?

关键在进程从用户态转到内核态的时候,进程的内核栈总是空的。这是因为,当进程在用户态运行时,使用的是用户栈,当进程陷入到内核态时,内核栈保存进程在内核态运行的相关信心,但是一旦进程返回到用户态后,内核栈中保存的信息无效,会全部恢复,因此每次进程从用户态陷入内核的时候得到的内核栈都是空的。所以在进程陷入内核的时候,直接把内核栈的栈顶地址给堆栈指针寄存器就可以了。
3.内核栈的实现

内核栈在kernel-2.4和kernel-2.6里面的实现方式是不一样的。
在kernel-2.4内核里面,内核栈的实现是:
union task_union {
struct task_struct task;
unsigned long stack[init_stack_size/sizeof(long)];
}; 其中,init_stack_size的大小只能是8k。

内核为每个进程分配task_struct结构体的时候,实际上分配两个连续的物理页面,底部用作task_struct结构体,结构上面的用作堆栈。使用current()宏能够访问当前正在运行的进程描述符。
注意:这个时候task_struct结构是在内核栈里面的,内核栈的实际能用大小大概有7k。

内核栈在kernel-2.6里面的实现是(kernel-2.6.32):

union thread_union {
struct thread_info thread_info;
unsigned long stack[thread_size/sizeof(long)];
}; 其中thread_size的大小可以是4k,也可以是8k,thread_info占52bytes。

当内核栈为8k时,thread_info在这块内存的起始地址,内核栈从堆栈末端向下增长。所以此时,kernel-2.6中的current宏是需要更改的。要通过thread_info结构体中的task_struct域来获得于thread_info相关联的task。更详细的参考相应的current宏的实现。
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
… ..
}; 注意:此时的task_struct结构体已经不在内核栈空间里面了。
如若满意,请点击右侧【采纳答案】,如若还有问题,请点击【追问】
希望我的回答对您有所帮助,望采纳!

~
o(∩_∩)o~