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


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,找到文件fsprocproc_misc.c,接下来开始分析了,我们从入口函数开始分析proc_misc_init

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

#ifdef CONFIG_PRINTK
	{
		struct proc_dir_entry *entry;
        // 创建一个文件 kmsg ,父目录是 proc_root
		entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
        //创建成功则同时提供相应的读写操作
		if (entry)
			entry->proc_fops = &proc_kmsg_operations;
	}
#endif


const 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
");
    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
");
    copy_to_user(myuser,mylog_buf,10);
    return 10;
}
static int hello_init(void)
{
    sprintf(mylog_buf,"this is a log buf
");
    ...
}

测试后发现一直打印,这是引文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
");
    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
", ++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
", ++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
");

	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!
");
	}
	if (argc != 2)
	{
		printf("Usage :
");
		printf("%s <on|off>
", 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

    mark

    //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;
    #endif
                    printk("<<<<pt reached>>>> 
    ");
            }
    
        }
        mylog_buf[pt_write]=c;
        pt_write=pt_add(pt_write);
         printk("put in %d  
    ",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>>>  
", 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;
#endif
                printk("<<<<pt reached>>>> 
");
        }

    }
    mylog_buf[pt_write]=c;
    pt_write=pt_add(pt_write);
     printk("put in %d  
",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
");
    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
", ++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
", ++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
");

	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!
");
	}
	if (argc != 2)
	{
		printf("Usage :
");
		printf("%s <on|off>
", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
        val  = 0;

	}
	write(fd, &val, 4);
	return 0;
}

测试

  1. 加载驱动

    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. 运行测试程序

    ./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如果等于头指针,说明一个缓存满,需要移动读指针,具体见图片分析

mark

原文地址:https://www.cnblogs.com/zongzi10010/p/10256059.html