1. 程式人生 > >驅動除錯(二)-環形緩衝區到檔案

驅動除錯(二)-環形緩衝區到檔案

目錄


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_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\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

    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>>>> \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;
#endif
                printk("<<<<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如果等於頭指標,說明一個快取滿,需要移動讀指標,具體見圖片分析

mark