0.前言
上一章(點擊返回上一章)完成了一個內核模塊的編寫,實現了在內核運行時的動態加載和卸載。
在模塊的開發調測過程中或者模塊運行過程中,可能需要打印內核模塊的變量的值或者想要動態開關模塊的運行日志打印,那么就需要一個與內核交互的接口來實現這個目的。
1.什么是/proc文件
proc 文件系統是一個虛擬文件系統(類似的還有sys文件系統),對內核模塊中的全局變量,我們都可以為其生成一個/proc文件,在控制臺(即應用層)對其進行讀寫操作。
2.編寫一個/proc文件
需求:某個模塊在客戶現場出問題了,需要記錄內核模塊的日志用于記錄運行情況。
分析:在函數被調用不頻繁的情況下,可以直接使用printk來打印函數運行的分支情況。但是printk操作很消耗性能,在會產生大量打印的情況下,很可能會由于一直在print,cpu得不到調度而出發系統重啟。因此需要實現一個能根據需求動態調整打印的方式。
if (debug_enable)
{printk("[%s:%d], come here\n", __func__, __LINE__);
}
如上的代碼片段,我們只要能控制debug_enable這個變量就能控制日志打印的輸出。
2.1 代碼實現
2.1.1 km_proc文件
一般開發中,會有多個proc文件創建的需求,對于這些創建、讀、寫邏輯都統一放到一個文件中進行,本項目就放在km_proc.c和km_proc.h中,其中最重要的km_proc.c文件內容如下:
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include "km_proc.h"/* 我們本次就是為此變量創建一個proc文件,并在控制臺讀寫它 */
extern int km_debug_enable;struct proc_dir_entry *km_proc_dir = NULL; /* km模塊文件夾,用于存放模塊的所有proc文件 */
struct proc_dir_entry *km_debug_proc_file = NULL; /* km_debug對應的proc文件 */static int km_debug_proc_show(struct seq_file* file, void* v)
{seq_printf(file, "km_debug_enable:%d\n", km_debug_enable);return 0;
}static int km_debug_proc_open(struct inode* inode, struct file* file)
{return single_open(file, km_debug_proc_show, NULL);
}static ssize_t km_debug_proc_write(struct file* file, const char __user *buffer, size_t count, loff_t *pos)
{char buf[2] = {0};if (count > 2){printk("error:please input 0 or 1");return -ENOSPC;}if (copy_from_user(buf, buffer, count)){return -EFAULT;}sscanf(buf, "%d", &km_debug_enable);printk("km_debug_enable:%d\n", km_debug_enable);return count;
}#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
static const struct proc_ops km_debug_proc_fops = {.proc_open = km_debug_proc_open,.proc_read = seq_read,.proc_release = single_release,.proc_write = km_debug_proc_write,
};
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0)
static const struct file_operations km_debug_proc_fops = {.owner = THIS_MODULE,.open = km_debug_proc_open,.read = seq_read,.release = single_release,.write = km_debug_proc_write,
};
#else
#error "Please make sure your kernel vision" /* proc文件相關結構體和接口與內核版本有關,需要單獨適配 */
#endifint km_init_proc(void)
{int ret = 0;/* 創建/proc/km文件夾 */km_proc_dir = proc_mkdir(KM_PROC_DIR, NULL);if (NULL == km_proc_dir) /* 創建失敗,退出 */{ret = -1;printk("proc_mkdir fail!\n");goto err;}/* 創建/proc/km/km_debug文件 */km_debug_proc_file = proc_create(KM_DEBUG_FILE, 0644, km_proc_dir, &km_debug_proc_fops);if (NULL == km_debug_proc_file){ret = -1;printk("create /proc/%s/%s fail!\n", KM_PROC_DIR, KM_DEBUG_FILE);goto err1;}printk("proc init success!\n");return ret;err1:/* 失敗,銷毀已經創建的文件 */remove_proc_entry(KM_PROC_DIR, NULL);
err:return ret;
}void km_exit_proc(void)
{/* 按反向順序一個個銷毀,即先銷毀文件、再銷毀文件夾 */if (km_debug_proc_file){remove_proc_entry(KM_DEBUG_FILE, km_proc_dir);km_debug_proc_file = NULL;}if (km_proc_dir){remove_proc_entry(KM_PROC_DIR, NULL);km_proc_dir = NULL;}printk("proc exit complete!\n");
}
其余變更見:github:kernel_module:為自己的內核模塊添加一個proc文件
2.2.2 編譯、運行、測試
# 源碼目錄下編譯
make# 插入模塊
insmod km.ko# 查看插入結果
lsmod
查看內核打印信息:
dmesg
可以看到:
查看變量km_debug_enable的值:
cat /proc/km/km_debug
輸出:
修改變量km_debug_enable的值:
sudo sh -c "echo 1 > /proc/km/km_debug"
可以看到修改已經生效了。
注意:修改km_debug_enable值時,如果直接輸入echo,會提示權限不夠,原因可見:https://blog.csdn.net/change_can/article/details/115128218
3. 其他
在上面代碼中,可以看到有如下的條件編譯指令:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0)#else
#error "Please make sure your kernel vision" /* proc文件相關結構體和接口與內核版本有關,需要單獨適配 */
#endif
在實際開發中,一份代碼可能需要運行在各種機器上,機器使用的內核版本有差異,而不同的內核版本各個接口、結構體定義可能會有差異,這時候就可以用這種條件編譯指令通過判斷內核版本來編譯不同的代碼分支,減小代碼維護的復雜度。