Kernel

核心空間記憶體釋放凍結核心

  • November 22, 2019

我正在編寫一個核心模組。從使用者空間讀取字節並寫回。

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。沒有保證,但它非常有幫助。你不需要重新編譯你的核心。

引用自:https://unix.stackexchange.com/questions/553500