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

简介: 上一节的程序很振奋人心,我们自己实现了一个myprintk打印函数。但是这个函数存在一个致命的缺陷,那就是只能使用一次cat /proc/mymsg命令来读取mylog_buf的值。这是因为读到最后会出现:mylog_r == mylog_w,表示缓冲区为空,下一次就不能在读到数据了。

上一节的程序很振奋人心,我们自己实现了一个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时,都会从入口处开始打印!
目录
相关文章
|
运维 Java API
nacos常见问题之Nacos读取配置文件失败如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
ly~
|
11月前
|
安全 生物认证 数据库
有哪些常见的身份验证错误和漏洞?
本文介绍了常见的网络安全问题,包括弱密码、密码重用、身份验证流程缺陷、会话管理问题和社会工程学攻击。具体涉及简单密码易被破解、多平台使用同一密码、缺乏多因素认证、身份验证绕过、会话劫持与固定、钓鱼攻击和伪装攻击等。这些问题可能导致用户信息泄露和系统安全风险。
ly~
1199 5
|
存储 缓存 Kubernetes
云原生时代下的12要素
现在各大企业都开始上云,上云有很多好处,其中之一就是有助于数字化转型。企业一旦上云,能大大提升数据搜集能力,计算能力调度及业务应用的整体能力,企业可以更容易的基于云化架构去做更多的场景落地。但上云并不意味着是真正的云原生(Cloud Native)。怎样才是真正的云原生呢?本文主要通过介绍云原生时代下应用需要遵循的12要素加以说明。
3946 0
|
NoSQL Linux 网络协议
GDB(十)--调试正在运行的进程
我编写了一个循环:     long i;    for (i = 0; i < 999999; i++) {        mt.a += 1;        sleep(1);    }把它编译成a.out,并在后台执行它:./a.out & [1] 2570 然后用命令gdb ./a.out 2570可以附加到这个进程上。
2264 0
|
Linux 内存技术
在Linux运行期间升级Linux系统(Uboot+kernel+Rootfs)
版本:v1.2   Crifan Li 摘要 本文主要介绍了如何在嵌入式Linux系统运行的时候,进行升级整个Linux系统,包括uboot,kernel和rootfs。
2193 0
|
NoSQL
gdb 调试多线程 神贴
gdb 调试多线程如果目标进程已经core dump了,那么 gdb -c core xxx   xxx是对应的程序文件。如果目标进程还在运行,通常此时用于调试线程死锁的情况。有两种方法一是 gdb -p xxx  xxx是该进程的进程ID或者用gcore xxx先获取对应进程的core,他会生成一个core文件 core.
2530 1
|
Unix Linux
U-boot mkimage指定Linux内核地址时的两种方式
uImage的制作是使用的u-boot工具mkimage,build完u-boot后也会将mkimage build出來到/tools目录下,可以直接拿來用,它的作用就是在zImage的前面加上64个字节的头,让u-boot能够识别要加载内核的类型、加载地址等。
1126 0
|
Linux
linux下so动态库一些不为人知的秘密(中)
上一篇(linux下so动态库一些不为人知的秘密(上))介绍了linux下so一些依赖问题,本篇将介绍linux的so路径搜索问题。      我们知道linux链接so有两种途径:显示和隐式。所谓显示就是程序主动调用dlopen打开相关so;这里需要补充的是,如果使用显示链接,上篇文章讨论的那些问题都不存在。
1663 0
|
NoSQL C语言 Shell
安装GDB和GDBSERVER
安装GDB和GDBSERVER 转自http://www.360doc.com/content/10/0407/17/155970_21971613.shtml 把GDBSERVER装入文件系统 转自http://www.
3292 0
|
NoSQL Linux
GDB如何调试没有符号表(未加-g选项的编译)的程序
/********************************************************************* * Author  : Samson * Date    : 01/30/2015 * Test platform: *              3.
3268 0