目录

  • 驱动调试(二)-环形缓冲区到文件

    • 目标
    • 框架分析
      • 虚拟文件系统proc
      • dmesg
      • proc_misc_init
      • kmsg_read
      • do_syslog
    • 程序1创建文件
    • 程序2提供读函数
    • 程序3读全局数组
    • 程序4 环形缓冲区+休眠唤醒
      • 打印驱动
      • 测试驱动
      • 应用程序
      • 测试
    • 程序5 cat后保留数据
      • 完整的程序
      • 测试驱动
      • 应用程序
      • 测试
    • 环形缓冲区
      • 读后清除
      • 读后不清除


title: 驱动调试(二)-环形缓冲区到文件
date: 2019/1/10 22:57:04
toc: true
---

驱动调试(二)-环形缓冲区到文件

目标

  • printk是将信息先保存到log_buf,然后通过打印级别来选择是否输出.
  • log_buf存储在/proc/kmsg中,该文件是包含了<x>打印级别的
  • 使用cat去获取这个文件是读后清的,使用dmsg是允许反复读的

参考上述的描述,尝试达成如下目标

  1. 构造一个my_log_bug[],存储到文件/proc/mymsg
  2. 提供read的接口供cat使用,使用环形缓冲区保存,提供读后清和读后不清的版本
  3. 驱动程序调用my_printk输出到my_log_bug写入

框架分析

虚拟文件系统proc

我们的/proc实际上是一个虚拟的文件系统,我们使用mount或者cat /proc/mount来查看挂接了哪些

# mount
rootfs on / type rootfs (rw)
/dev/root on / type yaffs (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
tmpfs on /dev type tmpfs (rw)
devpts on /dev/pts type devpts (rw)
# cat /proc/mounts
rootfs / rootfs rw 0 0
/dev/root / yaffs rw 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
tmpfs /dev tmpfs rw 0 0
devpts /dev/pts devpts rw 0 0

这个文件系统是我们在脚本文件中指挂载的,mount -a表示挂载所有/etc/fstab的文件系统

# cat /etc/init.d/rcS
mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s# cat /etc/fstab
#device mount-ponit type options dump fsck
proc    /proc   proc    defaults    0   0
sysfs   /sys    sysfs   defaults    0   0
tmpfs   /dev    tmpfs   defaults    0   0

dmesg

我们在printk中可以指定级别来输出打印,可以使用dmesg来查看所有的信息log_buf,这个命令实际是去读取文件/proc/kmsg,可以直接使用cat来读取这个信息

注意 这个文件只能cat一次,然后就清空了,使用dmesg可以多次查看的,使用cat命令是能够看到打印级别的

# cat /proc/kmsg
]=PATH=/sbin:/bin:/usr/sbin:/usr/bin
<4>envp[2]=ACTION=add
<4>envp[3]=DEVPATH=/class/tty/ttyw9
<4>envp[4]=SUBSYSTEM=tty

proc_misc_init

搜索kmsg,找到文件fs\proc\proc_misc.c,接下来开始分析了,我们从入口函数开始分析proc_misc_init

创建一个文件kmsg ,父目录是proc_root,创建成功则同时提供相应的读写操作

#ifdef CONFIG_PRINTK{struct proc_dir_entry *entry;// 创建一个文件 kmsg ,父目录是 proc_rootentry = create_proc_entry("kmsg", S_IRUSR, &proc_root);//创建成功则同时提供相应的读写操作if (entry)entry->proc_fops = &proc_kmsg_operations;}
#endifconst struct file_operations proc_kmsg_operations = {.read       = kmsg_read,.poll       = kmsg_poll,.open       = kmsg_open,.release    = kmsg_release,
};

参见程序1,创建mymsg目录

kmsg_read

  1. 判断如果是非阻塞方式打开,且没有数据,直接返回
  2. 如果是阻塞方式打开,等待读取
static ssize_t kmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))return -EAGAIN;return do_syslog(2, buf, count);
}// 非阻塞方式判断是否是空
do_syslog(9, NULL, 0))case 9:     /* Number of chars in the log buffer */error = log_end - log_start;break;//阻塞方式,进入休眠唤醒了case 2:     /* Read from log */error = -EINVAL;if (!buf || len < 0)goto out;error = 0;if (!len)goto out;if (!access_ok(VERIFY_WRITE, buf, len)) {error = -EFAULT;goto out;}//这里判断数据是否为空,wait_event_interruptible 中第二个参数为0是睡眠error = wait_event_interruptible(log_wait,(log_start - log_end));if (error)goto out;i = 0;spin_lock_irq(&logbuf_lock);while (!error && (log_start != log_end) && i < len) {c = LOG_BUF(log_start);log_start++;spin_unlock_irq(&logbuf_lock);error = __put_user(c,buf);buf++;i++;cond_resched();spin_lock_irq(&logbuf_lock);}spin_unlock_irq(&logbuf_lock);if (!error)error = i;break;

do_syslog

  • 非阻塞方式,直接看看属否有数据
  • 阻塞方式,数据为空则睡眠等待

程序1创建文件

仿照着写一个驱动,产生一个 my_msg 的文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.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/poll.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>struct proc_dir_entry *my_entry;
const  struct  file_operations proc_mymsg_operations;static int hello_init(void)
{my_entry = create_proc_entry("mymsg", S_IRUSR, &proc_root);if (my_entry)my_entry->proc_fops = &proc_mymsg_operations;return 0;
}
static void hello_exit(void)
{remove_proc_entry("mymsg",&proc_root);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

测试下,确实生成了文件,无法cat是因为没有提供读写函数

# insmod mymsg.ko
# ls /proc/mymsg -l
-r--------    1 0        0               0 Jan  5 04:38 /proc/mymsg
# cat /proc/mymsg
cat: read error: Invalid argument

程序2提供读函数

我们提供下读函数,避免cat报错

ssize_t *mymsg_read (struct file *  myfile , char __user *  myuser , size_t   len , loff_t * myloff )
{printk("print by mymsg\n");return 0;  //这里如果不return0 ,就一直打印了
}
const  struct  file_operations proc_mymsg_operations=
{.read=mymsg_read,
};

测试如下

# insmod mymsg.ko
# cat /proc/mymsg
print by mymsg

程序3读全局数组

这里提供一个全局数组,复制到用户态

struct proc_dir_entry *my_entry;
static char mylog_buf[1024];ssize_t *mymsg_read (struct file *  myfile , char __user *  myuser , size_t   len , loff_t * myloff )
{//printk("print by mymsg\n");copy_to_user(myuser,mylog_buf,10);return 10;
}
static int hello_init(void)
{sprintf(mylog_buf,"this is a log buf\n");...
}

测试后发现一直打印,这是引文read函数一直有返回,应该是cat后不断去read的原因

# cat /proc/mymsg
this is a this is a this is a this is a
this is a this is a this is a this is a 

程序4 环形缓冲区+休眠唤醒

环形缓冲区就是有头尾指针的一个数组,这里有一个巧妙的判断是否为满的方法

写的位置+1====读的位置,则是满
  • 空 读指针=写指针
  • 满 写指针=读指针+1

具体的函数如下

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;
}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;
}

接下来使用唤醒队列来处理,也就是当读取的时候如果没有数据,则睡眠,写数据的时候触发休眠的队列

static void mylog_putc(char c)
{写操作.../* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */
}

接着根据原有的.read=kmsg_read函数模仿写一个

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())return -EAGAIN;//阻塞方式 如果为空则睡眠error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());// 唤醒后,也就是有数据,读取数据复制到用户态while (!error && (mylog_getc(&c)) && i < count) {error = __put_user(c, buf);buf++;i++;}if (!error)error = i;return error;
}

创建一个printf函数,参考printk中将缓存赋值中使用了

printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
{int i;i=vsnprintf(buf,size,fmt,args);return (i >= size) ? (size - 1) : i;
}或者看下
int sprintf(char * buf, const char *fmt, ...)
{va_list args;int i;va_start(args, fmt);i=vsprintf(buf,fmt,args);va_end(args);return i;
}
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;
}

打印驱动

提供myprintk供其他驱动程序调用写入缓冲

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.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/poll.h>
#include <linux/cdev.h>#include <linux/proc_fs.h>
extern int myprintk(const char *fmt, ...);
EXPORT_SYMBOL(myprintk);static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);
struct proc_dir_entry *my_entry;
#define LEN_LOG 1024
static char mylog_buf[LEN_LOG];
static char tmp_buf[LEN_LOG];
static int pt_read=0,pt_write=0;#define pt_add(pt)    ((pt+1)%LEN_LOG)// ret =1 means empty
int  isEmpty(void)
{return (pt_read == pt_write);
}// ret =1 means full
int  isFull(void)
{return (pt_read == pt_add(pt_write));
}
//putchar
void myputc(char c)
{if (isFull()) {pt_read = pt_add(pt_read);}mylog_buf[pt_write]=c;pt_write=pt_add(pt_write);/* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */
}//getchar
int  mygetchar(char * p)
{if (isEmpty()) {return 0;}*p = mylog_buf[pt_read];pt_read=pt_add(pt_read);return 1;
}//printf for user
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++)myputc(tmp_buf[j]);return i;
}ssize_t *mymsg_read (struct file *  myfile , char __user *  buf , size_t   len , loff_t * myloff )
{int error = 0;int i = 0;char c;/* 把mylog_buf的数据copy_to_user, return */if ((myfile->f_flags & O_NONBLOCK) && isEmpty())return -EAGAIN;error = wait_event_interruptible(mymsg_waitq, !isEmpty());/* copy_to_user */while (!error && (mygetchar(&c)) && i < len) {error = __put_user(c, buf);buf++;i++;}if (!error)error = i;return error;
}const  struct  file_operations proc_mymsg_operations=
{.read=mymsg_read,
};static int hello_init(void)
{sprintf(mylog_buf,"this is a log buf\n");my_entry = create_proc_entry("mymsg", S_IRUSR, &proc_root);if (my_entry)my_entry->proc_fops = &proc_mymsg_operations;return 0;
}static void hello_exit(void)
{remove_proc_entry("mymsg",&proc_root);
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

测试驱动

调用myprintkwrite时写入缓冲

#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>static struct class *firstdrv_class;
static struct class_device  *firstdrv_class_dev;
extern int myprintk(const char *fmt, ...);
static int first_drv_open(struct inode *inode, struct file *file)
{static int cnt = 0;myprintk("first_drv_open : %d\n", ++cnt);return 0;
}static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{int val;static int cnt = 0;myprintk("first_drv_write : %d\n", ++cnt);return 0;
}static struct file_operations first_drv_fops = {.owner  =   THIS_MODULE,  .open   =   first_drv_open,     .write  =   first_drv_write,
};int major;
static int first_drv_init(void)
{myprintk("first_drv_init\n");major = register_chrdev(0, "first_drv", &first_drv_fops); firstdrv_class = class_create(THIS_MODULE, "firstdrv");firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); return 0;
}static void first_drv_exit(void)
{unregister_chrdev(major, "first_drv"); class_device_unregister(firstdrv_class_dev);class_destroy(firstdrv_class);
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");

应用程序

调用open打开测试驱动,使用write以调用myprintk写入缓冲


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>/* firstdrvtest on* firstdrvtest off*/
int main(int argc, char **argv)
{int fd;int val = 1;fd = open("/dev/xyz", O_RDWR);if (fd < 0){printf("can't open!\n");}if (argc != 2){printf("Usage :\n");printf("%s <on|off>\n", argv[0]);return 0;}if (strcmp(argv[1], "on") == 0){val  = 1;}else{val = 0;}write(fd, &val, 4);return 0;
}

测试

  1. 加载两个驱动

    # insmod ../mymsg.ko
    # insmod first_drv.ko
  2. 加载驱动程序

    # ./test on
    # ./test off
  3. 获取打印信息

    # cat /proc/mymsg &
    first_drv_init
    first_drv_open : 1
    first_drv_write : 1
    first_drv_open : 2
    first_drv_write : 2

程序5 cat后保留数据

在这里其实更应该理解成三个指针

  • 头指针,指向数据有效区域头

  • 尾指针,指向数据有效区的尾巴

  • 读指针,当前读取的区域

修改的部分

  1. 判断空的函数,应该判断读指针是否到达尾指针

    int  isEmpty(void)
    {return (pt_now_read == pt_write);
    }
  2. 读取函数,其中的读指针更改为这个新增的指针

    //getchar
    int  mygetchar(char * p)
    {if (isEmpty()) {return 0;}*p = mylog_buf[pt_now_read];pt_now_read=pt_add(pt_now_read);return 1;
    }
  3. 写数据的时候,如果写入的数据一次性超过缓冲区的大小,比如 缓冲区比较小,一次写入大于缓冲

    也就是比如当前是 start=3,end=2,now=2,存入数据后依然是start=3,end=2,now=2,这个时候需要手动调整now=start

    //putchar
    void myputc(char c)
    {if (isFull()) {pt_read = pt_add(pt_read);// 这里其实就是判断 当前读的指针在逻辑上必须大于有数据的 读的指针,也就是数据起始指针if (pt_add(pt_now_read) == pt_read) {
    #if(1)pt_now_read=pt_read;
    #endifprintk("<<<<pt reached>>>> \n");}}mylog_buf[pt_write]=c;pt_write=pt_add(pt_write);printk("put in %d  \n",pt_write);/* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */
    }

完整的程序

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.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/poll.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>
extern int myprintk(const char *fmt, ...);
EXPORT_SYMBOL(myprintk);
extern void  get_pt(void);
EXPORT_SYMBOL(get_pt);
static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);
struct proc_dir_entry *my_entry;
#define LEN_LOG 23
static char mylog_buf[LEN_LOG];
static char tmp_buf[LEN_LOG];
static int pt_read=0,pt_write=0;
static int pt_now_read=0;
//printf for user
void  get_pt(void )
{printk("<<<now is full  pt_read=%d pt_write=%d pt_now=%d>>>  \n", pt_read, pt_write, pt_now_read);
}
#define pt_add(pt)    ((pt+1)%LEN_LOG)
// ret =1 means empty
int  isEmpty(void)
{return (pt_now_read == pt_write);
}
// ret =1 means full
int  isFull(void)
{return (pt_read == pt_add(pt_write));
}
//putchar
void myputc(char c)
{if (isFull()) {pt_read = pt_add(pt_read);// 这里其实就是判断 当前读的指针在逻辑上必须大于有数据的 读的指针,也就是数据起始指针if (pt_add(pt_now_read) == pt_read) {
#if(1)pt_now_read=pt_read;
#endifprintk("<<<<pt reached>>>> \n");}}mylog_buf[pt_write]=c;pt_write=pt_add(pt_write);printk("put in %d  \n",pt_write);/* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */
}//getchar
int  mygetchar(char * p)
{if (isEmpty()) {return 0;}*p = mylog_buf[pt_now_read];pt_now_read=pt_add(pt_now_read);return 1;
}//printf for user
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++)myputc(tmp_buf[j]);return i;
}static int mymsg_open(struct inode *inode, struct file *file)
{pt_now_read = pt_read;return 0;
}ssize_t *mymsg_read (struct file *  myfile , char __user *  buf , size_t   len , loff_t * myloff )
{int error = 0;int i = 0;char c;/* 把mylog_buf的数据copy_to_user, return */if ((myfile->f_flags & O_NONBLOCK) && isEmpty())return -EAGAIN;error = wait_event_interruptible(mymsg_waitq, !isEmpty());/* copy_to_user */while (!error && (mygetchar(&c)) && i < len) {error = __put_user(c, buf);buf++;i++;}if (!error)error = i;return error;
}const  struct  file_operations proc_mymsg_operations=
{.read=mymsg_read,.open=mymsg_open,
};static int hello_init(void)
{sprintf(mylog_buf,"this is a log buf\n");my_entry = create_proc_entry("mymsg", S_IRUSR, &proc_root);if (my_entry)my_entry->proc_fops = &proc_mymsg_operations;return 0;
}
static void hello_exit(void)
{remove_proc_entry("mymsg",&proc_root);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

测试驱动

更改下测试驱动,使得有方法显示当前的指针 调用get_pt显示当前指针

#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>static struct class *firstdrv_class;
static struct class_device  *firstdrv_class_dev;
extern int myprintk(const char *fmt, ...);
static int first_drv_open(struct inode *inode, struct file *file)
{//static int cnt = 0;//myprintk(">>Open>>%d\n", ++cnt);return 0;
}static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{int val;static int cnt = 0;copy_from_user(&val,buf,count);if (val==0) {get_pt();}else{myprintk(">>1234567890123456Read>>%d\n", ++cnt);}return 0;
}static struct file_operations first_drv_fops = {.owner  =   THIS_MODULE,  .open   =   first_drv_open,     .write  =   first_drv_write,
};int major;
static int first_drv_init(void)
{//myprintk("first_drv_init\n");major = register_chrdev(0, "first_drv", &first_drv_fops); firstdrv_class = class_create(THIS_MODULE, "firstdrv");firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); return 0;
}static void first_drv_exit(void)
{unregister_chrdev(major, "first_drv"); class_device_unregister(firstdrv_class_dev);class_destroy(firstdrv_class);
}module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");

应用程序

更改下应用程序使得有方法显示当前的指针./test show

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>int main(int argc, char **argv)
{int fd;int val = 1;fd = open("/dev/xyz", O_RDWR);if (fd < 0){printf("can't open!\n");}if (argc != 2){printf("Usage :\n");printf("%s <on|off>\n", argv[0]);return 0;}if (strcmp(argv[1], "on") == 0){val  = 1;}else{val  = 0;}write(fd, &val, 4);return 0;
}

测试

  1. 加载驱动
    shell mount -t nfs -o nolock,vers=2 192.168.95.222:/home/book/stu /mnt insmod ../mymsg.ko && insmod first_drv.ko && cat /proc/mymsg & rmmod first_drv && rmmod mymsg echo "7 1 4 7 "> /proc/sys/kernel/printk
  2. 运行测试程序
    shell ./test on #写入缓冲区 ./test on1 # 显示当前的三个 头指针,尾指针,以及当前的读指针

  3. 测试错误的驱动,这里驱动(mymsg)程序,我测试了两个版本,一个是写数据的时候不判断是否一次就写满缓冲,另外一个是判断写缓冲的,可以发现不判断写缓冲的,打印输出不对

    # ./test show
    <<<now is full  pt_read=0 pt_write=0 pt_now=0>>>
    # ./test on
    put in 1
    put in 2
    put in 3
    put in 4
    put in 5
    put in 6
    put in 7
    put in 8
    put in 9
    put in 10
    put in 11
    put in 12
    put in 13
    put in 14
    put in 15
    put in 16
    put in 17
    put in 18
    put in 19
    put in 20
    put in 21
    put in 22
    <<<<pt reached>>>>
    put in 0
    put in 1
    put in 2
    put in 3
    >1                      ########这里打印明显出错了,缓冲区已经改变了起始位置
    # ./test show
    <<<now is full  pt_read=4 pt_write=3 pt_now=3>>>
  4. 测试正确的驱动程序

    # mount -t nfs -o nolock,vers=2 192.168.95.222:/home/book/stu /mnt
    #
    # cd /mnt/code/first_drv_myprintk/
    # insmod ../mymsg.ko && insmod first_drv.ko && cat /proc/mymsg &
    #
    # echo "7 1 4 7 "> /proc/sys/kernel/printk
    # ./test show
    <<<now is full  pt_read=0 pt_write=0 pt_now=0>>>
    # ./test on
    put in 1
    put in 2
    put in 3
    put in 4
    put in 5
    put in 6
    put in 7
    put in 8
    put in 9
    put in 10
    put in 11
    put in 12
    put in 13
    put in 14
    put in 15
    put in 16
    put in 17
    put in 18
    put in 19
    put in 20
    put in 21
    put in 22
    <<<<pt reached>>>>
    put in 0
    <<<<pt reached>>>>
    put in 1
    <<<<pt reached>>>>
    put in 2
    <<<<pt reached>>>>
    put in 3
    # 34567890123456Read>>1         #############打印正确# ./test show
    <<<now is full  pt_read=4 pt_write=3 pt_now=3>>>
    

环形缓冲区

读后清除

  1. 空 读指针=写指针
  2. 满 写指针=读指针+1

读后不清除

  1. 定义为头,尾指针,读指针
  2. 空 头指针=尾指针
  3. 满 头指针=尾指针+1
  4. 一般来说,缓冲区一直处于满的状态工作
  5. 第一次读取的时候直接从头指针开始获取
  6. 持续后台读取的时候,需要判断是否有一次性塞满一个缓存循环,也就是写入的时候,判断当前的读指针+1如果等于头指针,说明一个缓存满,需要移动读指针,具体见图片分析

转载于:https://www.cnblogs.com/zongzi10010/p/10256059.html

驱动调试(二)-环形缓冲区到文件相关推荐

  1. WinCE驱动调试助手V2.5

    http://www.cnblogs.com/we-hjb/archive/2008/12/15/1280822.html http://blog.chinaunix.net/u1/49088/sho ...

  2. GD32F303RET6 串口空闲中断+DMA数据发送接收+环形缓冲区方式保存数据

    GD32F303RET6 DMA 通道映射关系 串口 源文件 #include "uart.h" #include "stdio.h" #include &qu ...

  3. Windows驱动开发学习笔记(二)—— 驱动调试内核编程基础

    Windows驱动开发学习笔记(二)-- 驱动调试&内核编程基础 基础知识 驱动调试 PDB(Program Debug Database) WinDbg 加载 PDB 实验:调试 .sys ...

  4. RK3588 实现温控风扇之pwm驱动调试(二)

    RK3588 实现温控风扇系列文章 第一章: RK3588 实现温控风扇之如何获取cpu温度(一) 第二章: RK3588 实现温控风扇之pwm驱动调试(二) 第三章: RK3588 实现温控风扇之通 ...

  5. android 电容屏(二):驱动调试之基本概念篇

    关键词:android  电容屏 tp 工作队列 中断 多点触摸协议 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0  平台:S5PV310(sams ...

  6. 基于瑞芯微RV1109 Linux触摸屏GT911驱动调试心得(二)-设备树刷厂商给的触摸屏固件...

    之前调试触摸屏的文章如下: 基于瑞芯微RV1109 Linux触摸屏GT911驱动调试心得_Bruce.yang的嵌入式之旅-CSDN博客 由于之前调触摸屏一直都对不上点,然后厂商又给了我新的驱动代码 ...

  7. gdb 调试(二) 分析code文件

    借鉴学习https://blog.csdn.net/hanchaoman/article/details/5583457 博文 当遇到段错误的情况 方法1 :短小代码中单步调试 我们用gdb的step ...

  8. 嵌入式软件之裸板与驱动调试

    目录 一.裸板调试 1.1 点灯法 1.2 串口调试 1.3 JTAG原理 1.4 JTAG调试 二.驱动调试 2.1 printk调试 2.1.1 printk原理 2.1.2 printk的使用 ...

  9. 嵌入式Linux驱动笔记(二十五)------Input子系统框架

    你好!这里是风筝的博客, 欢迎和我一起交流. 一.Input子系统概述 二.Input子系统架构 三.Input子系统工作机制 3.1 核心层(input.c) 3.1.1 input_init函数 ...

最新文章

  1. Redis初学:5(String类型的基本操作)
  2. c++经典代码大全_宁海檩条C型钢型号大全
  3. pix4d怎么查看点云数据_python里怎么查看数据类型
  4. [转]布隆过滤器详解
  5. 微信能远程控制电脑吗_牛皮!微信远程控制电脑这个神器太厉害了!
  6. 实例4:python
  7. JSON语法之JSON键/值对
  8. 如何只用一个小时定制一个行业AI 模型?
  9. C语言浮点数的输出方法和示例
  10. jquery 省市区联动插件
  11. Excel 常用函数
  12. 所以,FileWriter和BufferedWriter的真正区别在哪
  13. Linux基线检查( 一)
  14. 国内5款优秀的WEB前端框架
  15. 非常棒的开源协同办公OA项目,收藏了!
  16. 中文版Google App Engine入门指南
  17. windows开机启动项设置
  18. Gopher转Rust辣眼睛语法排行榜
  19. 应用长时间置于后台后广播失效_无奈!终于知道为什么关闭了后台应用手机还是卡了!...
  20. 响应号召!中国北斗+国产GIS 打好基础软件国产化攻坚战

热门文章

  1. Gradle sync failed: failed to find Build Tools revision 21.1.2
  2. loadrunner安装补丁报错
  3. 【热门收藏】iOS开发人员必看的精品资料(100个)——下载目录
  4. 3年前的一个小项目经验,分享给菜鸟兄弟们(公文收发小软件:小技能 DeleteMark)...
  5. C#中自定义属性的例子
  6. Flutter framework在线上构建时的包大小优化
  7. 关于Oracle数据库19c中的关键字和保留字的说明
  8. Web应用渗透测试框架Arachni
  9. ArduinoYun快速入门教程第1章ArduinoYun概览
  10. python爬取微博指定内容_python3.5爬虫-爬取微博某博主微博内容