proc est un système de fichiers spécial dans le système Linux. Il est utilisé pour fournir une interface interactive entre le noyau et l'espace utilisateur, comme l'affichage des informations sur le noyau, la modification des paramètres du noyau, le contrôle des fonctions du noyau, etc. L’avantage de proc est qu’il est simple et facile à utiliser et ne nécessite aucun équipement ni pilote supplémentaire. L'implémentation de proc implique des concepts tels que la structure proc_dir_entry, la fonction proc_create et le mécanisme seq_file. Dans cet article, nous présenterons les principes et les méthodes d'auto-construction de proc dans la technologie de débogage du noyau Linux, y compris la création et la suppression de fichiers proc, la lecture et l'écriture de fichiers proc, l'utilisation du mécanisme seq_file, etc., et donnerons des exemples pour illustrer leur utilisation. et précautions.
L'utilisation de printk dans le noyau peut enregistrer les informations de débogage dans le tampon log_buf. Vous pouvez utiliser la commande #cat /proc/kmsg pour imprimer les données numériques dans la zone tampon. Aujourd'hui, nous allons l'étudier et écrire nous-mêmes le fichier kmsg. nous l'avons nommé mymsg.
Dans le fichier 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; ......................... }
Dans le fichier Kmsg.c (fsproc) :
const struct file_operations proc_kmsg_operations = { .read = kmsg_read, .poll = kmsg_poll, .open = kmsg_open, .release = kmsg_release, };
Lors de l'utilisation de cat /proc/kmsg dans l'espace utilisateur, kmsg_open sera appelé et la fonction kmsg_read sera appelée pour lire les données dans log_buf et les copier dans l'espace utilisateur pour les afficher.
La file d'attente en anneau est une structure de données extrêmement utile dans la programmation réelle. Elle présente les caractéristiques suivantes.
Il s'agit d'une structure de données FIFO connectée bout à bout, utilisant l'espace linéaire du tableau. L'organisation des données est simple et vous pouvez rapidement savoir si la file d'attente est pleine ou vide. Les données sont accessibles très rapidement.
En raison de leur simplicité et de leur efficacité, les files d'attente en anneau sont même implémentées dans le matériel.
Les files d'attente en anneau sont largement utilisées pour la transmission et la réception de données réseau, ainsi que pour l'échange de données entre différents programmes (par exemple, l'échange de grandes quantités de données entre le noyau et les applications et la réception de grandes quantités de données provenant du matériel).
Il n'y a pas de structure en anneau en mémoire, donc la file d'attente en anneau est en fait implémentée par l'espace linéaire du tableau. Alors que faire lorsque les données arrivent à leur fin ? Il reviendra à la position 0 pour le traitement. Cette conversion est effectuée à l'aide d'une opération modulo.
<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>
La clé de la file d'attente circulaire est de déterminer si la file d'attente est vide ou pleine. Lorsque la queue rattrape la tête, la file d'attente est pleine, et lorsque la tête rattrape la queue, la file d'attente est vide. Mais comment savoir qui rattrape qui. Certains moyens auxiliaires sont également nécessaires pour juger.
Comment juger si la file d'attente circulaire est vide ou pleine ? Il existe deux façons de juger.
1. Attachez une étiquette de drapeau
Lorsque la tête rattrape la queue et que la file d'attente est vide, définissez tag=0,
Lorsque la queue rattrape la tête et que la file d'attente est pleine, laissez tag=1,
2. Limitez la queue pour rattraper la tête, c'est-à-dire qu'il y a au moins un élément d'espace entre le nœud de queue et le nœud de tête de l'équipe.
La file d'attente est vide : head==tail
La file d'attente est pleine : (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");
Remarque : EXPORT_SYMBOL(myprintk) est utilisé dans le programme ci-dessus ; cela signifie que myprintk peut être utilisé dans tout l'espace du noyau.
使用方法:①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. Testez l'effet dans 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系统中一种有用而灵活的文件系统,它可以实现对内核的交互和反馈,也可以提升内核的可维护性和可扩展性。希望本文能够对你有所帮助和启发。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!