Linux系统中的内核交互文件系统:自构proc详解
proc是Linux系统中一种特殊的文件系统,它用来提供内核和用户空间的交互接口,如显示内核信息,修改内核参数,控制内核功能等。proc的优点是简单易用,不需要额外的设备或驱动。proc的实现涉及到proc_dir_entry结构体,proc_create函数,seq_file机制等概念。在本文中,我们将介绍Linux内核调试技术之自构proc的原理和方法,包括创建和删除proc文件,读取和写入proc文件,使用seq_file机制等,并举例说明它们的使用方法和注意事项。
1、简介
在内核中使用printk可以讲调试信息保存在log_buf缓冲区中,可以使用命令 #cat /proc/kmsg 将缓冲区的数区的数数据打印出来,今天我们就来研究一下,自己写kmsg这个文件,我们取名叫做 mymsg。
2、查看内核中 /proc/kmsg怎么写的!
在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; ......................... }
在Kmsg.c (fsproc) 文件中:
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中的数据,拷贝到用户空间显示。
3、在写之前,我们需要来学习一下循环队列
环形队列是在实际编程极为有用的数据结构,它有如下特点。
它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单,能很快知道队列是否满为空。能以很快速度的来存取数据。
因为有简单高效的原因,甚至在硬件都实现了环形队列。
环形队列广泛用于网络数据收发,和不同程序间数据交换(比如内核与应用程序大量交换数据,从硬件接收大量数据)均使用了环形队列。
3.1.环形队列实现原理
内存上没有环形的结构,因此环形队列实上是数组的线性空间来实现。那当数据到了尾部如何处理呢?它将转回到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

4、程序编写
#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");
5、测试程序
注意:在上面程序中 使用了 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中文网其他相关文章!

简介:通过基于Linux的道德黑客攻击数字边界 在我们越来越相互联系的世界中,网络安全至关重要。 道德黑客入侵和渗透测试对于主动识别和减轻脆弱性至关重要

Linux基础学习从零开始的方法包括:1.了解文件系统和命令行界面,2.掌握基本命令如ls、cd、mkdir,3.学习文件操作,如创建和编辑文件,4.探索高级用法如管道和grep命令,5.掌握调试技巧和性能优化,6.通过实践和探索不断提升技能。

Linux在服务器、嵌入式系统和桌面环境中的应用广泛。1)在服务器领域,Linux因其稳定性和安全性成为托管网站、数据库和应用的理想选择。2)在嵌入式系统中,Linux因其高度定制性和高效性而受欢迎。3)在桌面环境中,Linux提供了多种桌面环境,满足不同用户需求。

Linux的缺点包括用户体验、软件兼容性、硬件支持和学习曲线。1.用户体验不如Windows或macOS友好,依赖命令行界面。2.软件兼容性不如其他系统,缺乏许多商业软件的原生版本。3.硬件支持不如Windows全面,可能需要手动编译驱动程序。4.学习曲线较陡峭,掌握命令行操作需要时间和耐心。

Linuxisnothardtolearn,butthedifficultydependsonyourbackgroundandgoals.ForthosewithOSexperience,especiallycommand-linefamiliarity,Linuxisaneasytransition.Beginnersmayfaceasteeperlearningcurvebutcanmanagewithproperresources.Linux'sopen-sourcenature,bas

Linux的五个基本组件是:1.内核,管理硬件资源;2.系统库,提供函数和服务;3.Shell,用户与系统交互的接口;4.文件系统,存储和组织数据;5.应用程序,利用系统资源实现功能。

开启智能家居新篇章:基于Ubuntu的开源家庭自动化系统 智能家居技术彻底改变了我们与生活空间的互动方式,为日常生活带来了便利、安全和能源效率。从远程控制灯光和电器,到监控安全摄像头和自动化气候控制,智能家居技术变得越来越普及。 然而,许多商业智能家居系统存在局限性:高昂的成本、隐私问题以及有限的兼容性。幸运的是,开源软件解决方案结合Ubuntu的强大功能,提供了一种替代方案——允许用户创建可定制、经济高效且安全的智能家居生态系统。 本指南将探讨如何使用Ubuntu和开源工具设置家庭自动化系统。

Linux与Windows:2025比较 考虑从MacOS或Windows切换? Linux可能是答案。 尽管MacOS用户会发现相对平滑的过渡(由于MacOS的UNIX Core),但Windows用户需要适应。此指南HIG


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

SublimeText3 Linux新版
SublimeText3 Linux最新版

WebStorm Mac版
好用的JavaScript开发工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

Atom编辑器mac版下载
最流行的的开源编辑器