首先我们需要弄清楚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. Windows简单入门-送给第一次使用电脑的朋友

    序言 写本篇文章前.不得不说我已经好久没有写博客了,快接近3个月了 吧,本来去年说參加今年的博客之星的,结果这都立即结束了:不得不说对自己有些嘲讽. 本篇文章是纯小白文章.之所以写这个是由于前段时间妹 ...

  2. BZOJ 1027 JSOI2007 合金 计算几何+Floyd

    题目大意:给定一些合金,选择最少的合金,使这些合金能够按比例合成要求的合金 首先这题的想法特别奇异 看这题干怎么会想到计算几何 并且计算几何又怎么会跟Floyd挂边 好强大 首先因为a+b+c=1 所 ...

  3. 29.局部和全局重载new delete

    #include <iostream> #include <Windows.h> using namespace std; //全局内存管理,统计释放内存,分配内存 //重载全 ...

  4. mysql中load data Infile运用

    速度比insert要快20倍.共享一下java程序操作. package com.mysql.csv; import java.sql.Connection; import java.sql.Driv ...

  5. python 命令行參数解析

    本文是从我还有一个博客转载过来的,欢迎大家点击进去看一下,帮我添加点人气^_^ ImPyy 选择模块 依据python參考手冊的提示,optparse 已经废弃,应使用 argparse 教程 概念 ...

  6. 6.CPP风格数组

    //数组初始化 1 #include <iostream> using namespace std; void main() { //C int a[5] = { 1,2,3,4,5 }; ...

  7. 洛谷P1876 开灯

    题目背景 该题的题目是不是感到很眼熟呢? 事实上,如果你懂的方法,该题的代码简直不能再短. 但是如果你不懂得呢?那...(自己去想) 题目描述 首先所有的灯都是关的(注意是关!),编号为1的人走过来, ...

  8. HDF文件的显示策略

    作者:朱金灿 来源:http://blog.csdn.net/clever101 hdf格式(类似还有netcdf格式)格式是国际上通用的遥感数据格式.它们都是采用不规则存储的格式,就是在一个hdf文 ...

  9. JQuery 当点击input后,单选多选的选中状态

    1.当点击input元素,此元素的选中checked的值 = 此元素此时表现的选中与否的状态. eg:input元素开始是未选中,点击后表现的是选中状态,此元素的checked为true(和此元素开始 ...

  10. window.location无法跳转页面的问题

    最近在使用 window的location时碰到一个无法跳转页面的问题, 后来在location语句后加了一条这样的语句:window.event.returnValue = false;然后竟然可以 ...