上一节的程序很振奋人心,我们自己实现了一个myprintk打印函数。但是这个函数存在一个致命的缺陷,那就是只能使用一次cat /proc/mymsg命令来读取mylog_buf的值。这是因为读到最后会出现:mylog_r == mylog_w,表示缓冲区为空,下一次就不能在读到数据了。在本节里面我们就着手来解决这个问题,我们要实现的就是每次使用 cat /proc/mymsg 时,都会从头打印。那么我们就需要将入口做一个拷贝,一个保存起来,一个进行变换。这样的话,当下一次读的时候,我们可以将保存的入口重新做个拷贝,然后让拷贝进行变化。具体程序如下:

 
#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_r_for_read = 0;//这个用来拷贝mylog_r ,它将改变,但是mylog_r 不变
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_empty_for_read(void)
{
return (mylog_r_for_read == mylog_w);
}
 
static int is_mylog_full(void)
{
return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
}
 
//这个函数是被myprintk函数调用的
static void mylog_putc(char c)
{
if (is_mylog_full())
{
mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
                /*加上下面三行的原因是:如果读的时候,也一直在调用printk写的话,
                *当写的速度比较快的时候,可能会导致mylog_w超过mylog_r_for_read,
                *这时就需要更新mylog_r_for_read,使mylog_r_for_read 指向新的入口
                *当mylog_w超过入口mylog_r时,mylog_r会一直跟着更新的!        
               */
if ((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)
{
mylog_r_for_read = mylog_r;
}
}
 
mylog_buf[mylog_w] = c;
mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;
 
    wake_up_interruptible(&mymsg_waitq); 
}
 
static int mylog_getc_for_read(char *p)
{
if (is_mylog_empty_for_read())
{
return 0;
}
*p = mylog_buf[mylog_r_for_read];
mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN;
return 1;
}
 
 
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 < i; j++)
mylog_putc(tmp_buf[j]);
 
return i;
}
 
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;
 
if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read())
return -EAGAIN;
 
error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
 
/* copy_to_user */
while (!error && (mylog_getc_for_read(&c)) && i < count) {
error = __put_user(c, buf);
buf++;
i++;
}
 
if (!error)
error = i;
 
return error;
}
 
static int mymsg_open(struct inode *inode, struct file *file)
{
mylog_r_for_read = mylog_r;
return 0;
}
 
const struct file_operations proc_mymsg_operations = {
.open = mymsg_open,
.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");
 
总结,关于这个函数,当我们在用户空间,使用命令:cat /proc/mymsg时,首先会调用open函数,在open函数里面会将入口做一个拷贝,然后拿出一份来作为变化量,另外一个作为入口不改变。这样,每次cat /proc/mymsg时,都会从入口处开始打印!

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

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

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

  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. android组件

    SlidingDrawer http://www.cnblogs.com/renyuan/archive/2012/09/16/2687929.html autocompletetextview  / ...

  2. js的数据类型和typeof数据类型

    js的数据类型:number,string,null,undefined,Boolean,object typeof数据类型:number,string,object,function,undefin ...

  3. Appium_python3使用汇总

    1. 对webview页面元素的处理self.driver.switch_to.context("WEBVIEW_com.aaa.bbb")source = self.driver ...

  4. Shiro学习总结(2)——Apache Shiro快速入门教程

    第一部分 什么是Apache Shiro 1.什么是 apache shiro : Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理 如同 spr ...

  5. 不是IT圈人的IT创业优劣势!

    不是IT圈人的IT创业优势: 1)更尊重市场导向而非技术   2)更关注产品细节而非技术  3)更关注企业平衡而非技术 不是IT圈人的IT创业劣势: 1)因营销而放弃技术规划   2)因需求而丧失技术 ...

  6. windows下使用cpanm进行模块安装

    windows下使用cpanm进行模块安装 要放假了,突然想整理一下手头上的软件,突然发现perl的安装模块这个功能不能用. 弄了一下,使得windows 下 perl 的 cpanm能用,避免成天为 ...

  7. html中的瀑布流是什么

    html中的瀑布流是什么 一.总结 1.瀑布流: 从左往右排列,哪一列现在的总高度最小,就优先排序把item(单元格)放在这一列.这样排完所有的单元格后,可以保证每一列的总高度都相差不大 2.看效果图 ...

  8. Spark应用程序部署工具Spark Submit

    不多说,直接上干货!  spark-submit在哪个位置 [spark@master ~]$ cd $SPARK_HOME/bin [spark@master bin]$ pwd /usr/loca ...

  9. Anaconda的安装

    Windows下Anaconda的安装和简单使用 Anaconda is a completely free Python distribution (including for commercial ...

  10. Lucene 查询方式

    QueryParser类 QueryParser类对输入字符串的解析 格    式 含    义 “David” 在默认的字段中检索“David”关键字 “content:David” 在“conte ...