Heim >System-Tutorial >LINUX >Interaktives Kernel-Dateisystem im Linux-System: Detaillierte Erläuterung des selbst erstellten Prozesses
proc ist ein spezielles Dateisystem im Linux-System, das zur Bereitstellung einer interaktiven Schnittstelle zwischen dem Kernel und dem Benutzerbereich verwendet wird, z. B. zum Anzeigen von Kernel-Informationen, zum Ändern von Kernel-Parametern, zum Steuern von Kernel-Funktionen usw. Der Vorteil von proc besteht darin, dass es einfach und benutzerfreundlich ist und keine zusätzliche Ausrüstung oder Treiber erfordert. Die Implementierung von proc umfasst Konzepte wie die Struktur proc_dir_entry, die Funktion proc_create und den Mechanismus seq_file. In diesem Artikel stellen wir die Prinzipien und Methoden des selbstkonstruierenden Prozesses in der Linux-Kernel-Debugging-Technologie vor, einschließlich des Erstellens und Löschens von Proc-Dateien, des Lesens und Schreibens von Proc-Dateien, der Verwendung des seq_file-Mechanismus usw., und geben Beispiele zur Veranschaulichung ihrer Verwendung und Vorsichtsmaßnahmen.
Mit printk im Kernel können Sie Debugging-Informationen im Pufferbereich log_buf speichern. Sie können den Befehl #cat /proc/kmsg verwenden, um die numerischen Daten im Pufferbereich auszudrucken. wir haben es mymsg genannt.
In der Datei Proc_misc.c (fsproc):
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; ......................... }
In der Datei Kmsg.c (fsproc):
const struct file_operations proc_kmsg_operations = { .read = kmsg_read, .poll = kmsg_poll, .open = kmsg_open, .release = kmsg_release, };
Bei Verwendung von cat /proc/kmsg im Benutzerbereich wird kmsg_open aufgerufen und die Funktion kmsg_read wird aufgerufen, um die Daten in log_buf zu lesen und zur Anzeige in den Benutzerbereich zu kopieren.
Ring-Warteschlange ist eine äußerst nützliche Datenstruktur in der tatsächlichen Programmierung. Sie weist die folgenden Eigenschaften auf.
Es handelt sich um eine End-to-End-FIFO-Datenstruktur, die den linearen Raum des Arrays nutzt. Die Datenorganisation ist einfach und Sie können schnell erkennen, ob die Warteschlange voll oder leer ist. Der Zugriff auf die Daten ist sehr schnell möglich.
Aufgrund der Einfachheit und Effizienz werden Ringwarteschlangen sogar in Hardware implementiert.
Ringwarteschlangen werden häufig zum Senden und Empfangen von Netzwerkdaten sowie zum Datenaustausch zwischen verschiedenen Programmen verwendet (z. B. zum Austausch großer Datenmengen zwischen dem Kernel und Anwendungen und zum Empfang großer Datenmengen von der Hardware).
Es gibt keine Ringstruktur im Speicher, daher wird die Ringwarteschlange tatsächlich durch den linearen Raum des Arrays implementiert. Was also tun, wenn die Daten das Ende erreichen? Zur Bearbeitung wird es wieder in die Position 0 gedreht. Diese Umkehrung erfolgt über eine Modulo-Operation.
<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>
Der Schlüssel zur kreisförmigen Warteschlange besteht darin, festzustellen, ob die Warteschlange leer oder voll ist. Wenn der Schwanz den Kopf einholt, ist die Warteschlange voll, und wenn der Kopf den Schwanz einholt, ist die Warteschlange leer. Aber wie erkennt man, wer wen einholt? Zur Beurteilung werden auch einige Hilfsmittel benötigt.
Wie kann man beurteilen, ob die kreisförmige Warteschlange leer oder voll ist? Es gibt zwei Möglichkeiten, dies zu beurteilen.
1. Bringen Sie ein Flaggenetikett an
Wenn der Kopf den Schwanz einholt und die Warteschlange leer ist, setzen Sie tag=0,
Wenn der Schwanz den Kopf einholt und die Warteschlange voll ist, sei tag=1,
2. Begrenzen Sie den Schwanz, um den Kopf einzuholen, das heißt, zwischen dem Schwanzknoten und dem Kopfknoten des Teams befindet sich mindestens ein Raumelement.
Die Warteschlange ist leer: head==tail
Die Warteschlange ist voll: (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");
Hinweis: Im obigen Programm wird EXPORT_SYMBOL(myprintk) verwendet, was bedeutet, dass myprintk im gesamten Kernelbereich verwendet werden kann.
使用方法:①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. Testen Sie die Wirkung in 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系统中一种有用而灵活的文件系统,它可以实现对内核的交互和反馈,也可以提升内核的可维护性和可扩展性。希望本文能够对你有所帮助和启发。
Das obige ist der detaillierte Inhalt vonInteraktives Kernel-Dateisystem im Linux-System: Detaillierte Erläuterung des selbst erstellten Prozesses. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!