博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
驱动程序调试方法之printk——自制proc文件(一)
阅读量:2188 次
发布时间:2019-05-02

本文共 5541 字,大约阅读时间需要 18 分钟。

首先我们需要弄清楚proc机制,来看看fs/proc/proc_misc.c这个文件,从入口函数开始看:

proc_misc_init(void)        #ifdef CONFIG_PRINTK{    struct proc_dir_entry *entry;    entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);//这里创建了一个proc入口kmsg    if (entry)    entry->proc_fops = &proc_kmsg_operations;//设置操作函数,见注释1}
注释1:
const struct file_operations proc_kmsg_operations = {    .read= kmsg_read,    .poll= kmsg_poll,    .open= kmsg_open,    .release= kmsg_release,};
这个操作函数就用于对kmsg来进行操作
于是我们可以仿照来设计一下!
#include 
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
#include
struct proc_dir_entry *myentry;const struct file_operations proc_mymsg_operations = {};static int mymsg_init(void){ myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root); if (myentry) myentry->proc_fops = &proc_mymsg_operations ; return 0;}void mymsg_eixt(void){ remove_proc_entry("mymsg", &proc_root);} module_init(mymsg_init);module_exit(mymsg_eixt);
这个函数只是在proc目录下面创建了一个入口而已!我们加载后:
ls /proc/mymsg -l
打印出如下信息:
-r--------    1 0        0               0 Feb  4 13:37 /proc/mymsg
如果我们想查看/proc/mymsg内容的话:
# cat /proc/mymsg
打印出来
cat: read error: Invalid argument
这也是理所当然的,因为我们根本没有读函数嘛!
所以我们接下来要做的就是来完成这个读函数,而这个读函数里面我们要做的就是将mylog_buf中的数据拷贝到用户空间。有一个很关键的地方就是,我们的 mylog_buf 应该是一个环形队列,关于环形队列的概念我们先来说一下:
front指向队列首部,rear指向队列尾部,size表示队列长度。读只能从首部读,写的话只能从尾部写!写到尾部的话,就回去重头开始写!
当front==rear的时候,可以判断队列是空的。
当(rear+1)/size==front的时候,可以判断队列是满的。
程序如下:
#include 
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
#include
#define MYLOG_BUF_LEN 1024struct proc_dir_entry *myentry;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 DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);//判断环形缓冲区是否为空static int is_mylog_empty(void){ return (mylog_r == mylog_w);}//判断环形缓冲区是否已满static int is_mylog_full(void){ return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);}/*写缓冲区:如果缓冲区已满的话,就让覆盖掉下一个要读的数据* 否则就直接写入此外在写缓冲区函数里面还需要做的一件事情就是唤醒等待队列,这是因为当缓冲区为空的时候,如果调用读函数的话,就会使进程进入等待队列,理当在写入数据的时候唤醒进程*/static void mylog_putc(char c){ if (is_mylog_full()) { /* 丢弃一个数据 */ mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; } mylog_buf[mylog_w] = c; mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN; /* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq); /* 唤醒休眠的进程 */}/*读缓冲区:如果缓冲区为空的话,就返回0否则从首部读出一个数据,返回1*/static int mylog_getc(char *p){ if (is_mylog_empty()) { return 0; } *p = mylog_buf[mylog_r]; mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; return 1;}/*打印函数:这个函数是参考sprintf函数得编写的*它将传递进来的参数转换为固定的格式之后,放入到一个临时缓冲区里面*然后将环形缓冲区的值写入到mylog_buf缓冲区里面,详见注释2*/int myprintk(const char *fmt, ...){ va_list args; int i; int j; va_start(args, fmt); i = vsnprintf(tmp_buf, INT_MAX, fmt, args); //将传进来的参数转换后放入tmp_buf va_end(args); for (j = 0; j < i; j++) mylog_putc(tmp_buf[j]); //将tmp_buf里面的东东放入mylog_buf缓冲区里面 return i;}/*读函数:当在应用空间调用命令:cat /proc/mymsg的时候,会调用这个函数**/static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ int error = 0; int i = 0; char c; /* 把mylog_buf的数据copy_to_user, return */ //如果为非阻塞且mylog_buf为空,那么就出错返回 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) return -EAGAIN; //如果mylog_buf为空的话进程进入等待队列,还记得我们在写缓冲区 //函数里面会唤醒进程这件事情吧! error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty()); /* copy_to_user */ //首先从缓冲区里面获得一个字符,然后拷贝到用户空间 //如果缓冲区还有信息的话,就再次获得字符,拷贝到用户 //空间,直到缓冲区为空 while (!error && (mylog_getc(&c)) && i < count) { error = __put_user(c, buf); //将c的内容拷贝到用户空间 buf++; i++; } if (!error) error = i; return error;}const struct file_operations proc_mymsg_operations = { .read = mymsg_read,};static int mymsg_init(void){ 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");
/*因为
myprintk是我们自己写的打印语句
*所以需要导出才能被使用,使用的时候还需要声明一下:
extern int myprintk(const char *fmt,...);
*/
EXPORT_SYMBOL(myprintk);
MODULE_LICENSE("GPL");
我们在来总结一下:在本文件里面我们做了两件事情,一件事情是定义了一个写函数,当我们在用户空间使用命令:cat /proc/mymsg的时候,就会调用到这个读函数,这个读函数会将mylog_buf中的数据拷贝到用户空间,那么mylog_buf里面的数据哪里来的呢?这就是我们做的另外一件事情,我们定义了一个打印函数,这个打印函数会将要打印的数据写入一个临时缓冲区,然后又从临时缓冲区里面取出数据放入mylog_buf中。cat /proc/mymsg的候就会将mylog_buf中的数据拷贝到用户空间,就可以显示出来了!
注释2:
int sprintf(char * buf, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsnprintf(buf, INT_MAX, fmt, args);
va_end(args);
return i;
}
这个就是sprintf函数,它将传递进来的参数放入buf缓冲区,我们做的就是将这个缓冲区里面的值在放入mylog_buf缓冲区里面!相当的巧妙啊!
接下来我们不妨测试一下,测试函数如下:
#include 
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
extern int myprintk(const char *fmt, ...);static int first_drv_init(void){ myprintk("first_drv_init\n"); return 0;}static void first_drv_exit(void){ myprintk("abcdefhg\n");}module_init(first_drv_init);module_exit(first_drv_exit);MODULE_LICENSE("GPL");
上两个C文件要放到同一个目录下,Makefile如下:
obj-m := kproctest3.o kproctest.oKERNELDIR := /lib/modules/2.6.23-gentoo-r9/build/PWD :=$(shell pwd)modules:	$(MAKE)  -C  $(KERNELDIR)  M=$(PWD)  modules.PHONY :cleanclean:	rm -rf *.o *ko
先加载proc.ko
在加载test.ko
cat /proc/mymsg,打印如下信息:
first_drv_init
这正是我们打印的信息,成功了!

转载地址:http://hjzub.baihongyu.com/

你可能感兴趣的文章
iOS MapKit导航及地理转码辅助类
查看>>
检测iOS的网络可用性并打开网络设置
查看>>
简单封装FMDB操作sqlite的模板
查看>>
iOS开发中Instruments的用法
查看>>
iOS常用宏定义
查看>>
什么是ActiveRecord
查看>>
有道词典for mac在Mac OS X 10.9不能取词
查看>>
关于“团队建设”的反思
查看>>
利用jekyll在github中搭建博客
查看>>
Windows7中IIS简单安装与配置(详细图解)
查看>>
linux基本命令
查看>>
BlockQueue 生产消费 不需要判断阻塞唤醒条件
查看>>
强引用 软引用 弱引用 虚引用
查看>>
数据类型 java转换
查看>>
"NetworkError: 400 Bad Request - http://172.16.47.117:8088/rhip/**/####t/approval?date=976
查看>>
mybatis 根据 数据库表 自动生成 实体
查看>>
win10将IE11兼容ie10
查看>>
checkbox设置字体颜色
查看>>
第一篇 HelloWorld.java重新学起
查看>>
ORACLE表空间扩张
查看>>