我正在编写一个内核模块。从用户空间读取字节并写回。
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
Node *msg;
int error_count = 0;
// Entering critical section
down(&sem); //wait state
msg = pop(&l, 0);
// No message? No wait!
if(!msg) {
up(&sem);
return -EAGAIN;
}
len = msg->length;
error_count = copy_to_user(buffer, msg->string, msg->length);
if (error_count == 0) {
current_size -= msg->length;
remove_element(&l, 0);
up(&sem);
return 0;
} else {
up(&sem);
printk(KERN_INFO "opsysmem: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
Node *n;
// buffer larger than 2 * 1024 bytes
if(len > MAX_MESSAGE_SIZE || len == 0) {
return -EINVAL;
}
n = kmalloc(sizeof(Node), GFP_KERNEL);
if(!n) {
return -EAGAIN;
}
n->string = (char*) kmalloc(len, GFP_KERNEL);
n->length = len;
copy_from_user(n->string, buffer, len);
// Enter critical section
down(&sem); //wait state
// buffer is larger than the total list memory (2MiB)
if(current_size + len > MAX_LIST_SIZE) {
up(&sem);
return -EAGAIN;
}
current_size += len;
push(&l, n);
up(&sem);
// Exit critical section
return len;
}
销毁应该释放链表的函数
static void __exit opsysmem_exit(void) {
// Deallocate the list of messages
down(&sem);
destroy(&l);
up(&sem);
device_destroy(opsysmemClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(opsysmemClass); // unregister the device class
class_destroy(opsysmemClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "charDeviceDriver: Goodbye from the LKM!\n");
}
我的链表和销毁函数如下所示:
static void destroyNode(Node *n) {
if(n) {
destroyNode(n->next);
kfree(n->string);
n->string = NULL;
kfree(n);
n = NULL;
}
}
static void destroy(list *l){
if(l) {
destroyNode(l->node);
}
}
typedef struct Node {
unsigned int length;
char* string;
struct Node *next;
} Node;
typedef struct list{
struct Node *node;
} list;
问题如下:
我写入设备驱动程序,我想要rmmod
驱动程序,并且opsysmem_exit
应该调用 kfree() 所有内存。
当我有少量节点时,这有效。
如果我运行大量节点(1000+)并且尝试使用 rmmode,虚拟机就会冻结。
您知道为什么以及我还应该做什么来诊断这个问题吗?
我的函数是否创建了太多级别的递归?
如果我写入2000000个节点然后读回它们,似乎没有问题。只要我 rmmod 时列表为空,一切正常。
编辑1:我注意到如果我执行 rmmod 而不释放内存,内核不会崩溃。但是,所有分配的内存都被泄漏,如下所示克德尔
答案1
我刚刚解决了。默里·詹森是对的。正是递归杀死了我的内核。
有人能解释一下为什么我花了7个小时来学习这个吗?现实中C的最大递归深度是多少?我今天早上读了一篇文章,上面写着 523756我在这里读到了,向下滚动到 C。
这是我的解除分配器。您可能已经注意到,零泄漏。
static void destroy2(list *l) {
Node *_current = l->node;
Node *_next;
while(_current) {
_next = _current->next;
kfree(_current->string);
kfree(_current);
_current = _next;
}
}
我在主帖中使用的递归方法的另一个坏处是,它会随机跳过 kfree-ing 2 到 4 个节点。
对于任何对我的泄漏检查报告感兴趣的人:我正在使用我在 github 上发现的开源工具https://github.com/euspecter/kedr。没有任何保证,但它非常有帮助。您不需要重新编译内核。