Kernel
核心空間記憶體釋放凍結核心
我正在編寫一個核心模組。從使用者空間讀取字節並寫回。
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,核心不會崩潰。但是,所有分配的記憶體都洩漏了,如kedr所示
我剛剛解決了。默里詹森是對的。是遞歸殺死了我的核心。
有人可以解釋為什麼我花了 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。沒有保證,但它非常有幫助。你不需要重新編譯你的核心。