首先我们需要弄清楚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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/proc_fs.h>
 
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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/proc_fs.h>
 
#define MYLOG_BUF_LEN 1024
 
struct 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);
 
/*因为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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
 
xtern 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");
 
先加载proc.ko
在加载test.ko
cat /proc/mymsg,打印如下信息:
first_drv_init
这正是我们打印的信息,成功了!

驱动程序调试方法之printk——自制proc文件(一)的更多相关文章

  1. 驱动程序调试方法之printk——自制proc文件(二)

    上一节的程序很振奋人心,我们自己实现了一个myprintk打印函数.但是这个函数存在一个致命的缺陷,那就是只能使用一次cat /proc/mymsg命令来读取mylog_buf的值.这是因为读到最后会 ...

  2. 驱动程序调试方法之printk——printk的原理与直接使用

    1.基本原理 (1)在UBOOT里设置console=ttySAC0或者console=tty1 这里是设置控制终端,tySAC0 表示串口, tty1 表示lcd(2)内核用printk打印 内核就 ...

  3. linux设备驱动学习笔记--内核调试方法之printk

    1,printk类似于用户态的printf函数,但是比printf函数多了一个日志级别,内核中最常见的日志输出都是通过调用printk来实现的,其打印级别有8种可能的记录字串, 在头文件 <Li ...

  4. ARM驱动调试方法、思路总结、笔记

    驱动程序的调试一. 打印: prink, 自制proc文件UBOOT传入console=ttySAC0 console=tty11. 内核处理UBOOT传入的参数console_setup add_p ...

  5. 如何用Bat批处理自制自解压文件

    转载▼http://blog.sina.com.cn/s/blog_48462a890102e0nu.html     1.在桌面上新建一个文本文档,如:“新建 文本文档.txt”,方法是:在桌面的空 ...

  6. linux设备驱动程序第四部分:从如何定位oops对代码的调试方法,驱动线

    在一个我们谈到了如何编写一个简单的字符设备驱动程序,我们不是神,编写肯定会失败的代码,在这个过程中,我们需要继续写代码调试.在普通c应用.我们经常使用printf输出信息.或者使用gdb要调试程序,然 ...

  7. ubuntu/linux mint 创建proc文件的三种方法(四)

    在做内核驱动开发的时候,能够使用/proc下的文件,获取对应的信息,以便调试. 大多数/proc下的文件是仅仅读的,但为了演示样例的完整性,都提供了写方法. 方法一:使用create_proc_ent ...

  8. ubuntu/linux mint 创建proc文件的三种方法(两)

    在这样做的内核驱动程序的开发时间.可以使用/proc下档.获取相应的信息.对于调试. 大多数/proc下的文件是仅仅读的.但为了演示样例的完整性.都提供了写方法. 方法一:使用create_proc_ ...

  9. linux下core文件调试方法

    http://www.cnblogs.com/li-hao/archive/2011/09/25/2190278.html 在程序不寻常退出时,内核会在当前工作目录下生成一个core文件(是一个内存映 ...

随机推荐

  1. 基于Linux系统的Nagios网络管理模块的实现

    基于Linux 系统的Nagios网络管理模块的实现 1.引言 随着计算机网络的普及,网络管理已成为信息时代中最重要的问题之一.在现有的技术条件下,人们希望有一个更加稳定可靠的网络环境.计算机网络管理 ...

  2. 快速傅里叶变换FFT(模板)

    好不容易闲下来总结一下FFT.QAQ 1.DFT: 对于多项式的乘法,DFT给了我们新的思路(点值表达式的O(n)相乘) 对于我们习惯的多项式算法例如多项式A(x)=5x+1和B(x)=2x+2 C( ...

  3. 导入数据库工具有关的oracle sql操作

    alter table TAB_user add (personal varchar2(30) default '用户名' not null);alter table TAB_user modify ...

  4. POJ 3009 Curling 2.0 {深度优先搜索}

    原题 $On Planet MM-21, after their Olympic games this year, curling is getting popular. But the rules ...

  5. js进阶 14-8 表单序列化函数serializeArray()和serialize()的区别是什么

    js进阶 14-8 表单序列化函数serializeArray()和serialize()的区别是什么 一.总结 一句话总结:两者都是对表单进行序列化,serializeArray()返回的是json ...

  6. VUE笔记 - 品牌后台 - v-for Splice Some Filter findIndex indexOf 直接return函数结果

    <body> <div id="app"> <div class="panel panel-primary"> <di ...

  7. PatentTips - Method, apparatus and system for instructing a virtual device from a virtual machine

    BACKGROUND OF THE INVENTION A virtual machine (VM) may be or include a framework or environment crea ...

  8. jni和C++通信中文乱码的问题

    转自 http://www.cnblogs.com/bluesky4485/archive/2011/12/13/2285802.html 首先,需要明确几个关于编码的基本概念: java内部是使用的 ...

  9. 洛谷—— P1018 乘积最大

    https://www.luogu.org/problem/show?pid=1018#sub 题目描述 今年是国际数学联盟确定的“2000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年 ...

  10. js进阶 12-17 jquery实现鼠标左键按下拖拽功能

    js进阶 12-17 jquery实现鼠标左键按下拖拽功能 一.总结 一句话总结:监听的对象必须是文档,鼠标按下运行mousemove事件,鼠标松开取消mousemove事件的绑定,div的偏移的话是 ...