仿照printk编写自己的打印函数到缓冲区

我们知道printk分为两步,一是把要输出的信息放到缓冲区log_buf,另外一个是通过控制台输出信息。

这里构造myprintk函数,仿照printk的输出信息到缓冲区,并且把缓冲区对应/proc/mymsg文件节点,当cat    /proc/msg的时候会输出缓冲区信息

关于log_buf缓冲区的重要一点就是,该缓冲区是一个环形缓冲区(当然,这里用一个数组当作环形缓冲区),

 

该环形缓冲区有两个指针,读指针,写指针,

当写入一个数据的时候,写指针+1,  写入表示存储数据,等待读出

当读出一个数据的时候,读指针+1   读出表示该读出数据已经没用了

另外,环形缓冲区还有两个状态:空(empty),满(full)

当读指针等于写指针的时候,表示为缓冲区空

      w==r

 

当写指针在读指针后面的时候表示满,当然,一般情况w是不可能在r后面的,因为必须先写进去才能读,

出现这种情况是因为写数据写满了缓冲区,还有一个位置就到了r,也就是

  (w+1)%size==read

 

 

如果缓冲区已经满了之后,还要写入数据,那么读指针跟写指针都要+1

  w=(w+1)%size

  r= (r+1)%size

 

代码如下:

View Code
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/gpio.h>

#include <linux/poll.h>

#include <linux/sched.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/proc_fs.h>
//#include <asm-generic/Uaccess.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_w = 0;
static int mylog_r = 0;

static DECLARE_WAIT_QUEUE_HEAD(mylog_waitq);

static int is_mylog_empty(void)
{
    return (mylog_w==mylog_r);
}

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;
        
        /*唤醒等待数据的进程*/
        wake_up_interruptible(&mylog_waitq);
}

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

int myprintk(const char *fmt, ...)
{
    va_list args;
    int i,j;
    
    va_start(args,fmt);                /*具体可了解可变参数的函数处理*/
    i=vsnprintf(tmp_buf,INT_MAX,fmt,args);    /*解析参数,把所得的字符串放到tmp_buf缓存区*/
    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;
    /*把mylog_buf的数据copy_to_user,return,
     *对于cat命令,如果碰到\n则会停止,否则循环输出
     */

    if((file->f_flags&O_NONBLOCK)&&is_mylog_empty())    /*如果以非阻塞方式打开,并且mylog_buf是空的*/
        return -EAGAIN;
        
    error=wait_event_interruptible(mylog_waitq,!is_mylog_empty());    /*否则等待数据输入(mylog_buf非空)*/
    
    while (!error && (mylog_getc(&c)) && i < count) {    /*如果没错误,并且能从mylog_buf取出一个字符,并且请求长度大于i*/
            error = __put_user(c,buf);        /*返回取出的字符给用户空间*/
            buf++;
            i++;
        }
        if (!error)
            error = i;
                
    return error;
}

static const struct file_operations proc_mymsg_operations = {
    .read = mymsg_read,
};

static int mymsg_init(void)
{
    //proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
    
    myentry = create_proc_entry("mymsg",S_IRUSR,NULL);    /*在proc目录下创建条目*/
    if(myentry)
        myentry->proc_fops = &proc_mymsg_operations;    /*设置该条目的fops为proc_mymsg_operations*/
    return 0;
}

static void mymsg_exit(void)
{
    remove_proc_entry("mymsg",NULL);
}

EXPORT_SYMBOL(myprintk);

module_init(mymsg_init);
module_exit(mymsg_exit);

MODULE_LICENSE("GPL");

 

测试代码如下:

View Code
#include <linux/module.h>


extern int myprintk(const char *fmt, ...);

static int test_init(void)
{
    static int i = 0;
    myprintk("test_init\n");
    myprintk("count = %d\n",i);
    
    return 0;
}

static void test_exit(void)
{
    myprintk("test_exit\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

 

上面的代码是,当缓冲区内的数据被读出来后就处于无用状态了,并不能重复读取,

如果要实现重复读取,我们可以设置多一个指针r_head(“头指针”),让每次cat   /proc/mymsg时都从“头指针”所指的位置重新读取数据

也就是在open函数内   r = r_head

 

透着针遵循的移动规则如下:

在缓冲区没满的时候,头指针一直指向缓冲区头部

当缓冲区满的时候,头指针如前面所说的读指针 r 一样向后移动

 

改进后代码如下:

View Code
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/gpio.h>

#include <linux/poll.h>

#include <linux/sched.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/proc_fs.h>
//#include <asm-generic/Uaccess.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_w = 0;
static int mylog_r = 0;
static int mylog_r_head = 0;

static DECLARE_WAIT_QUEUE_HEAD(mylog_waitq);

static int is_mylog_empty(void)
{
    return (mylog_w==mylog_r);
}

static int is_mylog_full(void)
{
    return (((mylog_w+1)%MYLOG_BUF_LEN)==mylog_r_head);
}

static void mylog_putc(char c)
{
    if(is_mylog_full())
        {
            /*如果环形缓冲区满,丢弃一个数据*/
            mylog_r_head = (mylog_r_head+1)%MYLOG_BUF_LEN;
        }
        mylog_buf[mylog_w]=c;
        mylog_w = (mylog_w+1)%MYLOG_BUF_LEN;
        
        /*唤醒等待数据的进程*/
        wake_up_interruptible(&mylog_waitq);
}

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

int myprintk(const char *fmt, ...)
{
    va_list args;
    int i,j;
    
    va_start(args,fmt);                /*具体可了解可变参数的函数处理*/
    i=vsnprintf(tmp_buf,INT_MAX,fmt,args);    /*解析参数,把所得的字符串放到tmp_buf缓存区*/
    va_end(args);
    
    for(j=0;j<i;j++)
    mylog_putc(tmp_buf[j]);
    
    return i;
}

static int mymsg_open(struct inode *inode,struct file *file)
{
     /*每次cat /proc/mymsg都会open
        *这里mylog_r = mylog_r_head 表示每次都把r设置为“头指针”,从头开始读数据
        */
        mylog_r = mylog_r_head;        
        return 0;
}

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;
    
    /*把mylog_buf的数据copy_to_user,return,
     *对于cat命令,如果碰到\n则会停止,否则循环输出
     */

    if((file->f_flags&O_NONBLOCK)&&is_mylog_empty())    /*如果以非阻塞方式打开,并且mylog_buf是空的*/
        return -EAGAIN;
        
    error=wait_event_interruptible(mylog_waitq,!is_mylog_empty());    /*否则等待数据输入(mylog_buf非空)*/
    
    while (!error && (mylog_getc(&c)) && i < count) {    /*如果没错误,并且能从mylog_buf取出一个字符,并且请求长度大于i*/
            error = __put_user(c,buf);        /*返回取出的字符给用户空间*/
            buf++;
            i++;
        }
        if (!error)
            error = i;
                
    return error;
}

static const struct file_operations proc_mymsg_operations = {
    .open = mymsg_open,
    .read = mymsg_read,
};

static int mymsg_init(void)
{
    //proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
    
    myentry = create_proc_entry("mymsg",S_IRUSR,NULL);    /*在proc目录下创建条目*/
    if(myentry)
        myentry->proc_fops = &proc_mymsg_operations;    /*设置该条目的fops为proc_mymsg_operations*/
    return 0;
}

static void mymsg_exit(void)
{
    remove_proc_entry("mymsg",NULL);
}

EXPORT_SYMBOL(myprintk);

module_init(mymsg_init);
module_exit(mymsg_exit);

MODULE_LICENSE("GPL");

 

 

posted @ 2012-12-30 03:34  TaigaComplex  阅读(683)  评论(0编辑  收藏  举报