proc是Linux系統中一種特殊的檔案系統,它用來提供內核和用戶空間的交互接口,如顯示內核訊息,修改內核參數,控制內核功能等。 proc的優點是簡單易用,不需要額外的設備或驅動。 proc的實作涉及proc_dir_entry結構體,proc_create函數,seq_file機制等概念。在本文中,我們將介紹Linux內核調試技術之自構proc的原理和方法,包括創建和刪除proc文件,讀取和寫入proc文件,使用seq_file機制等,並舉例說明它們的使用方法和注意事項。
#在核心中使用printk可以講調試資訊保存在log_buf緩衝區中,可以使用命令#cat /proc/kmsg 將緩衝區的數區的數資料列印出來,今天我們就來研究一下,自己寫kmsg這個文件,我們取名叫做mymsg。
在Proc_misc.c (fs\proc) 檔案中:
void __init proc_misc_init(void) { ......................... struct proc_dir_entry *entry; //这里创建了一个proc入口kmsg entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); if (entry) /*构造一个proc_fops结构*/ entry->proc_fops = &proc_kmsg_operations; ......................... }
在Kmsg.c (fs\proc) 檔案中:
const struct file_operations proc_kmsg_operations = { .read = kmsg_read, .poll = kmsg_poll, .open = kmsg_open, .release = kmsg_release, };
在使用者空間使用 cat /proc/kmsg的時候,會呼叫kmsg_open,在呼叫kmsg_read函數,讀取log_buf中的數據,並拷貝到使用者空間顯示。
#環形佇列是在實際程式設計極為有用的資料結構,它有以下特點。
它是一個首尾相連的FIFO的資料結構,採用陣列的線性空間,資料組織簡單,很快就能知道佇列是否滿為空。能以很快速度的來存取資料。
因為有簡單且有效率的原因,甚至在硬體都實作了環形佇列。
環形佇列廣泛用於網路資料收發,和不同程式間資料交換(例如核心與應用程式大量交換數據,從硬體接收大量資料)均使用了環形佇列。
記憶體上沒有環形的結構,因此環形佇列實在是數組的線性空間來實現。那當數據到了尾部要如何處理呢?它將轉回到0位置來處理。這個的轉回是透過取模操作來執行的。
<code style="display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px">因此环列队列的是逻辑上将数组元素q[0]与q[MAXN-1]连接,形成一个存放队列的环形空间。 为了方便读写,还要用数组下标来指明队列的读写位置。head/tail.其中head指向可以读的位置,tail指向可以写的位置。 </code>
環形佇列的關鍵是判斷佇列為空,還是為滿。當tail追上head時,佇列為滿時,當head追上tail時,佇列為空。但如何知道誰追上誰。還需要一些輔助的手段來判斷.
如何判斷環形隊列為空,為滿有兩種判斷方法。
一.是附加一個標誌位元tag
當head趕上tail,隊列空,則令tag=0,
當tail趕上head,隊列滿,則令tag=1,
二.限制tail趕上head,即隊尾結點與隊首結點之間至少留有一個元素的空間。
佇列空: head==tail
隊列滿: (tail 1)% MAXN ==head
#include \#include \#include \#include \#include \#include \#include \#include \#include \#include \#include \#define MYLOG_BUF_LEN 1024 static char mylog_buf[MYLOG_BUF_LEN]; static char tmp_buf[MYLOG_BUF_LEN]; static int mylog_r = 0; static int mylog_w = 0; static int mylog_r_tmp = 0; /*休眠队列初始化*/ static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq); /* *判断环形队列是否为空 *返回0:表示不空 返回1:表示空 */ static int is_mylog_empty(void) { return (mylog_r == mylog_w); } /* *判断环形队列是否满 *返回0:表示不满 返回1:表示满 */ static int is_mylog_full(void) { return((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r); } /* *在读取的时候,判断环形队列中数据是否为空 *返回0:表示不空 返回1:表示空 */ static int is_mylog_empty_for_read(void) { return (mylog_r_tmp == mylog_w); } /* *往循环队列中存字符 *输入:c字符 单位:1byte *输出:无 */ static void mylog_putc(char c) { if(is_mylog_full()) { /*如果检测到队列已经满了,则丢弃该数据*/ mylog_r= (mylog_r + 1) % MYLOG_BUF_LEN; /*mylog_r_tmp不能大于mylog_r*/ if((mylog_r_tmp + 1)% MYLOG_BUF_LEN == mylog_r) mylog_r_tmp= mylog_r; } mylog_buf[mylog_w]= c; /*当mylog_w=1023的时候 (mylog_w+1) % MYLOG_BUF_LEN =0,回到队列头,实现循环*/ mylog_w= (mylog_w + 1) % MYLOG_BUF_LEN; /* 唤醒等待数据的进程*/ wake_up_interruptible(&mymsg_waitq); } /* *从循环队列中读字符 *输入:*p 单位:1byte *输出:1表示成功 */ static int mylog_getc(char *p) { /*判断数据是否为空*/ if (is_mylog_empty_for_read()) { return 0; } *p = mylog_buf[mylog_r_tmp ]; mylog_r_tmp = (mylog_r_tmp + 1) % MYLOG_BUF_LEN; return 1; } /* *调用myprintk,和printf用法相同 */ int myprintk(const char *fmt, ...) { va_list args; int i; int j; va_start(args, fmt); i= vsnprintf(tmp_buf, INT_MAX, fmt, args); va_end(args); for (j = 0; j return i; } static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t*ppos) { int error=0; size_t i=0; char c; /* 把mylog_buf的数据copy_to_user, return*/ /*非阻塞 和 缓冲区为空的时候返回*/ if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) return -EAGAIN; /*休眠队列wait_event_interruptible(xxx,0)-->休眠*/ error= wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read()); /* copy_to_user*/ while (!error && (mylog_getc(&c)) && i if (!error) error= i; /*返回实际读到的个数*/ return error; } static int mymsg_open(struct inode * inode, struct file * file) { mylog_r_tmp= mylog_r; return 0; } const struct file_operations proc_mymsg_operations = { .read= mymsg_read, .open= mymsg_open, }; static int mymsg_init(void) { struct proc_dir_entry *myentry; kmsg myentry= create_proc_entry("mymsg", S_IRUSR, &proc_root); if (myentry) myentry->proc_fops = &proc_mymsg_operations; return 0; } static void mymsg_exit(void) { remove_proc_entry("mymsg", &proc_root); } module_init(mymsg_init); module_exit(mymsg_exit); /*声名到内核空间*/ EXPORT_SYMBOL(myprintk); MODULE_LICENSE("GPL");
#注意:在上面程式中 使用了 EXPORT_SYMBOL(myprintk);意思是把myprintk可以在整個核心空間使用。
使用方法:①extern int myprintk(const char *fmt, ...);声明 ② myprintk("first_drv_open : %d\n", ++cnt);使用 \#include \#include \#include \#include \#include \#include \#include \#include \#include \#include static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev; volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; extern int myprintk(const char *fmt, ...); static int first_drv_open(struct inode *inode, struct file *file) { static int cnt = 0; myprintk("first_drv_open : %d\n", ++cnt); /* 配置GPF4,5,6为输出*/ *gpfcon &= ~((0x3return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; static int cnt = 0; myprintk("first_drv_write : %d\n", ++cnt); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1) { // 点灯 *gpfdat &= ~((1else { // 灭灯 *gpfdat |= (1return 0; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量*/ .open = first_drv_open, .write = first_drv_write, }; int major; static int first_drv_init(void) { myprintk("first_drv_init\n"); major= register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核 firstdrv_class= class_create(THIS_MODULE, "firstdrv"); firstdrv_class_dev= class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz*/ gpfcon= (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat= gpfcon + 1; return 0; } static void first_drv_exit(void) { unregister_chrdev(major,"first_drv"); // 卸载 class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); iounmap(gpfcon); } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");
6、在tty中測試效果
# insmod my_msg.ko``# insmod first_drv.ko``# cat /proc/mymsg``mymsg_open mylog_r_ tmp=0``first_drv_init
通过本文,我们了解了Linux内核调试技术之自构proc的原理和方法,它们可以用来实现对内核的调试和控制。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如使用正确的文件名,使用正确的读写函数,使用正确的seq_file操作等。proc是Linux系统中一种有用而灵活的文件系统,它可以实现对内核的交互和反馈,也可以提升内核的可维护性和可扩展性。希望本文能够对你有所帮助和启发。
以上是Linux系統中的核心互動檔案系統:自構proc詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!