34.Linux-printk解析ca88亚洲城网站、使用printk调试驱动, printk()会将打印音信存在根本的环形缓冲区log

在上章34.Linux-printk解析、使用printk调试驱动里描述了:

在上章34.Linux-printk剖析、使用printk调试驱动里描述了:

35.Linux-分析并制作环形缓冲区,35.linux-环形

在上章34.Linux-printk解析、使用printk调试驱动里描述了:

   
 printk()会将打印信息存在根本的环形缓冲区log_buf[]里, 可以通过dmesg命令来查看log_buf[]

 

 


1.环形缓冲区log_buf[]又是存在根本的哪位文件呢?

置身/proc/kmsg里,所以除了dmesg命令查看,也得以动用cat /proc/kmsg来查看

 

2.只是,dmesg命令和cat /proc/kmsg有所不同

2.1 dmesg命令

历次使用,都会打印出环形缓冲区的所有音信

2.2 cat /proc/kmsg

只会打印出每一趟新的环形缓冲区的信息

例如,第一次采纳cat /proc/kmsg,会打印出基础启动的持有新闻

其次次利用cat /proc/kmsg,就不会现出在此以前打印的信息,只打印继上次拔取cat
/proc/kmsg之后的新的音讯,比如下图所示:

 ca88亚洲城网站 1

 

3.接下来我们便进入基础,找/proc/kmsg文件在哪生成的

探寻”kmsg”,找到位于fs\proc\proc_misc.c
文件的proc_misc_init()函数中,

该函数重要用来生成登记的装置文件,具体代码如下所示:

const struct file_operations proc_kmsg_operations = {
       .read              = kmsg_read,               //读函数
       .poll        = kmsg_poll,
       .open             = kmsg_open,
       .release   = kmsg_release,
};


void __init proc_misc_init(void)
{
  ... ...
  struct proc_dir_entry *entry;                                   // 用来描述文件的结构体,
         entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); //使用create_proc_entry()创建文件
if (entry)
         entry->proc_fops = &proc_kmsg_operations;    //对创建的文件赋入file_ operations
    ... ...
}

从地点代码得出,/proc/kmsg文件,也是有file_operations结构体的,而cat命令就会一贯读/proc/kmsg的file_operations->read(),实现读log_buf[]的数据

且/proc/kmsg文件是经过create_proc_entry()创制出来的,参数如下所示:

“kmsg”:文件名

&proc_root:父目录,表示存在/proc根目录下

S_IRUSR:
等于400,表示拥有者(usr)可读,其他任什么人不可以举办任何操作,如下图所示:

 ca88亚洲城网站 2

该参数和chmod命令参数一样,除了S_IRUSR还有为数不少参数,比如:

S_IRWXU: 等于700, 表示拥有者(usr)可读(r)可写(w)可举行(x)

S_IRWXG: 等于070, 表示拥有者和组用户 (group)可读(r)可写(w)可实施(x)

 

4.为何选取dmesg命令和cat /proc/kmsg会有这么大的区分?

大家进入proc_kmsg_operations-> kmsg_read()看看,就知晓了

static ssize_t kmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
       /*若在非阻塞访问,且没有读的数据,则立刻return*/
       if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
              return -EAGAIN;
       return do_syslog(2, buf, count);          //开始读数据,buf:用户层地址,count:要读的数据长度
}

5.proc_kmsg_operations-> kmsg_read()->**do_syslog(9, NULL,
0)**的内容如下所示:

 ca88亚洲城网站 3

其中log_start和log_end就是环形缓冲区的五个标志,
log_start也可以称为读标志位,
log_end也足以叫做写标志位,当写标志和读标志一致时,则象征从没读的多少了。

6.proc_kmsg_operations-> kmsg_read()->**do_syslog(2, buf,
count)**的始末如下所示:

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)) {      // access_ok:检查用户层地址是否访问OK
                     error = -EFAULT;
                     goto out;
              }

              /*若没有读的数据,则进入等待队列*/
              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_BUF:取环形缓冲区log_buf[]里的某个位置的数据
                     log_start++;                        //读地址++
                     spin_unlock_irq(&logbuf_lock);
                     error = __put_user(c,buf);            //和 copy_to_user()函数一样,都是上传用户数据
                     buf++;                                       //用户地址++
                     i++;                                        //读数据长度++
                     cond_resched();
                     spin_lock_irq(&logbuf_lock);
              }
              spin_unlock_irq(&logbuf_lock);
              if (!error)
                     error = i;
              break;}
out:
       return error;
}

眼看就是对环形缓冲区的读操作,而环形缓冲区的原理又是怎么着?

7.接下来便来分析环形缓冲区的法则

和方面函数一样,
环形缓冲区需要一个大局数组,还索要几个标志:读标志R、写标志W

俺们以一个大局数组my_buff[7]为例,来分析:

7.1环形缓冲区开头时:

int R=0;             //记录读的位置
int W=0;             //记录写的位置    

地点的代码,如下图1所示:

 ca88亚洲城网站 4

 

R:从数组[R]先河读数据

W:从数组[W]起来写多少

因而,当R==W时,则意味不曾多少可读,通过这些逻辑便能写出读数据了

7.2当我们需要读数据时:

int read_buff(char  *p)              //p:指向要读出的地址
{
if(R==W)            
      return 0;       //读失败
*p=my_buff[R];
R=(R+1)%7;      //R++    
return  1;         //读成功  
}

我们以W=3,R=0,为例,调用3次read_buff()**函数,**如下图所示:

 ca88亚洲城网站 5

 

读数据完成,剩下就是写多少了,很明确每写一个数码,W则++

7.3所以写多少函数为:

void write_buff(char c)              //c:等于要写入的内容
{
  my_buff [W]=c;         
  W=(W+1)%7;    //W++          
if(W==R)
  R=(R+1)%7;      //R++
}

7.3.1 上边的代码,为何要判断if((W==R)?

比如,当我们写入一个8个数据,而my_buff[]只得存7个数据,必定会有W==R的时候,如果不加该论断,功用图如下所示:

 ca88亚洲城网站 6

 

接下来大家再反复调用read_buff(),就会发觉只读的出第8个数据的值,而眼前7个数据都会被撇下掉

7.3.2 而参预判断后,意义图如下所示:

 ca88亚洲城网站 7

 

然后我们再反复调用read_buff(),就可以读出my_buff [2]~ my_buff
[0]共6个数据出来

总结:

由于read_buff()后,R都会+1,所以每便 cat /proc/kmsg ,
都会清空上次的打印新闻。

 

8.环形缓冲区分析完毕后,大家就足以平一向写一个驱动,模仿/proc/kmsg文件来探望

流程如下:

  • 1)概念全局数组my_buff[1000]环形缓冲区,R标志,W标明,然后提供写函数,读函数
  • 2)自制一个myprintk(),通过传播的数目来放入到my_buff[]环形缓冲区中
  • (PS:需要通过EXPORT_SYMBOL(myprintk)表明该myprintk,否则不可能被其余驱动程序调用
    )
  • 3)写入口函数
  •    ->3.1) 通过create_proc_entry()创建/proc/mykmsg文件
  •    ->3.2 )并向mykmsg文件里添加file_operations结构体
  • 4)写出口函数
  •    ->4.1) 通过remove_proc_entry()卸载/proc/mykmsg文件
  • 5)file_operations->read()函数
  •  
     ->5.1) 
    仿照/proc/kmsg的read()函数,来读my_buff[]环形缓冲区的数额

现实代码如下所示:

#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 my_buff_len   1000          //环形缓冲区长度

static struct proc_dir_entry *my_entry;


/*    声明等待队列类型中断 mybuff_wait      */
static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);


static char my_buff[my_buff_len]; 
unsigned long R=0;                      //记录读的位置
unsigned long W=0;                    //记录写的位置

int read_buff(char  *p)         //p:指向要读出的地址
{
   if(R==W)          
             return 0;               //读失败
        *p=my_buff[R]; 
         R=(R+1)%my_buff_len;       //R++
        return  1;                   //读成功   
}

void write_buff(char c)          //c:等于要写入的内容
{    
        my_buff [W]=c;       
        W=(W+1)%my_buff_len;     //W++
        if(W==R)
            R=(R+1)%my_buff_len;     //R++
       wake_up_interruptible(&mybuff_wait);     //唤醒队列,因为R != W 
}

/*打印到my_buff[]环形缓冲区中*/
int myprintk(const char *fmt, ...)
{
       va_list args;
       int i,len;
       static char temporary_buff[my_buff_len];        //临时缓冲区
       va_start(args, fmt);
       len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
       va_end(args);

        /*将临时缓冲区放入环形缓冲区中*/
       for(i=0;i<len;i++)       
       {
            write_buff(temporary_buff[i]);
       }
       return len;
}

static int mykmsg_open(struct inode *inode, struct file *file)
{
        return 0;
}  

static int mykmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
      int error = 0,i=0;
      char c;

        if((file->f_flags&O_NONBLOCK)&&(R==W))      //非阻塞情况下,且没有数据可读
            return  -EAGAIN;

       error = -EINVAL;

              if (!buf || !count )
                     goto out;

       error = wait_event_interruptible(mybuff_wait,(W!=R));
              if (error)
                     goto out;

       while (!error && (read_buff(&c)) && i < count) 
      {
        error = __put_user(c,buf);      //上传用户数据
        buf ++;
        i++;
      }

      if (!error)
               error = i;
out:
       return error;
}  

const struct file_operations mykmsg_ops = {
       .read              = mykmsg_read,
       .open         = mykmsg_open,
};
static int  mykmsg_init(void)
{
    my_entry = create_proc_entry("mykmsg", S_IRUSR, &proc_root);
   if (my_entry)
         my_entry->proc_fops = &mykmsg_ops;
    return 0;
}
static void mykmsg_exit(void)
{
        remove_proc_entry("mykmsg", &proc_root);  
}

module_init(mykmsg_init);
module_exit(mykmsg_exit); 
EXPORT_SYMBOL(myprintk);
MODULE_LICENSE("GPL");

 

PS:当其他驱动向利用myprintk()打印函数,还索要在文书中声称,才行:

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

且还索要先装载mykmsg驱动,再来装载要动用myprintk()的驱动,否则无法找到myprintk()函数

 

9.测试运行

一般来说图所示,挂载了mykmsg驱动,可以见见变化了一个/proc/mykmsg文件

 ca88亚洲城网站 8

挂载/proc/mykmsg期间,此外驱动使用myprintk()函数,就会将消息打印在/proc/mykmsg文件中,如下图所示:

  ca88亚洲城网站 9

和cat /proc/kmsg一样,每回cat 都会清上两回的打印数据

10.若我们不想每回清,和dmesg命令一样,
历次都能打印出环形缓冲区的保有音讯,该怎么改mykmsg驱动?

上次大家分析过了,每回调用read_buff()后,R都会+1。

要想不清空上次的音信打印,还需要定义一个R_
current标志来代替R标志,那样每趟cat截止后,R的地方保持不变。

老是cat时,系统除去进入file_operations->
read(),还会进来file_operations-> open(),所以在open()里,使R_
current=R,然后在修改部分代码即可,

10.1我们如故以一个大局数组my_buff[7]为例, 正如图所示:

ca88亚洲城网站 10

10.2就此,修改的代码如下所示:

#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 my_buff_len   1000          //环形缓冲区长度

static struct proc_dir_entry *my_entry;

/*    声明等待队列类型中断 mybuff_wait      */
static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);

static char my_buff[my_buff_len];

unsigned long R=0;                      //记录读的位置
unsigned long R_current=0;             //记录cat期间 读的位置
unsigned long W=0;                    //记录写的位置

int read_buff(char  *p)         //p:指向要读出的地址
{
        if(R_current==W)             
             return 0;               //读失败
        *p=my_buff[R_current]; 
         R_current=(R_current+1)%my_buff_len;     //R_current++
        return  1;                   //读成功   
}

void write_buff(char c)          //c:等于要写入的内容
{    
        my_buff [W]=c;       
        W=(W+1)%my_buff_len;     //W++
        if(W==R)
          R=(R+1)%my_buff_len;     //R++
        if(W==R_current)
          R=(R+1)%my_buff_len;     //R_current++
       wake_up_interruptible(&mybuff_wait);     //唤醒队列,因为R !=W 
}

/*打印到my_buff[]环形缓冲区中*/
int myprintk(const char *fmt, ...)
{
  va_list args;
  int i,len;
  static char temporary_buff[my_buff_len];        //临时缓冲区
  va_start(args, fmt);
  len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
  va_end(args);
     /*将临时缓冲区放入环形缓冲区中*/
       for(i=0;i<len;i++)       
       {
            write_buff(temporary_buff[i]);
       }
  return len;
}

static int mykmsg_open(struct inode *inode, struct file *file)
{
        R_current=R;       
        return 0;
}  

static int mykmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
      int error = 0,i=0;
      char c;     
        if((file->f_flags&O_NONBLOCK)&&(R_current==W))      //非阻塞情况下,且没有数据可读
            return  -EAGAIN;
        error = -EINVAL;
       if (!buf || !count )
              goto out;

       error = wait_event_interruptible(mybuff_wait,(W!=R_current));
       if (error)
              goto out;

      while (!error && (read_buff(&c)) && i < count) 
      {
        error = __put_user(c,buf);      //上传用户数据
        buf ++;
        i++;
      }

      if (!error)
        error = i;

out:
  return error;
}  

const struct file_operations mykmsg_ops = {
  .read         = mykmsg_read,
  .open         = mykmsg_open,

};

static int  mykmsg_init(void)
{
    my_entry = create_proc_entry("mykmsg", S_IRUSR, &proc_root);
   if (my_entry)
    my_entry->proc_fops = &mykmsg_ops;
    return 0;
}
static void mykmsg_exit(void)
{
   remove_proc_entry("mykmsg", &proc_root);
}

module_init(mykmsg_init);
module_exit(mykmsg_exit); 
EXPORT_SYMBOL(myprintk); 
MODULE_LICENSE("GPL"); 

 

11.测试运行

 ca88亚洲城网站 11

 

 

 

http://www.bkjia.com/Linuxjc/1235659.htmlwww.bkjia.comtruehttp://www.bkjia.com/Linuxjc/1235659.htmlTechArticle35.Linux-分析并制作环形缓冲区,35.linux-环形
在上章 34.Linux-printk剖析、使用printk调试驱动 里讲述了: printk()会将打印消息存在根本的环形缓…

     printk()会将打印音信存在根本的环形缓冲区log_buf[]里, 可以由此dmesg命令来查看log_buf[]

     printk()会将打印消息存在根本的环形缓冲区log_buf[]里, 可以由此dmesg命令来查看log_buf[]

 

 

 

 



1.环形缓冲区log_buf[]又是存在根本的哪个文件呢?

1.环形缓冲区log_buf[]又是存在根本的哪个文件呢?

座落/proc/kmsg里,所以除了dmesg命令查看,也可以接纳cat /proc/kmsg来查阅

坐落/proc/kmsg里,所以除了dmesg命令查看,也能够利用cat /proc/kmsg来查看

 

 

2.不过,dmesg命令和cat /proc/kmsg有所不同

2.只是,dmesg命令和cat /proc/kmsg有所不同

2.1
dmesg命令

2.1
dmesg命令

老是使用,都会打印出环形缓冲区的拥有消息

历次使用,都会打印出环形缓冲区的所有音信

2.2
cat /proc/kmsg

2.2
cat /proc/kmsg

只会打印出每便新的环形缓冲区的音讯

只会打印出每趟新的环形缓冲区的音讯

比如,第三次使用cat
/proc/kmsg,会打印出基础启动的有着音讯

比如说,第一次采纳cat
/proc/kmsg,会打印出基础启动的拥有消息

第二次拔取cat
/proc/kmsg,就不会冒出此前打印的音信,只打印继上次使用cat
/proc/kmsg之后的新的信息,比如下图所示:

其次次选用cat
/proc/kmsg,就不会出现以前打印的消息,只打印继上次使用cat
/proc/kmsg之后的新的音信,比如下图所示:

 ca88亚洲城网站 12

 ca88亚洲城网站 13

 

 

3.接下来我们便进入基础,找/proc/kmsg文件在哪生成的

3.接下来大家便进入基础,找/proc/kmsg文件在哪生成的

摸索”kmsg”,找到位于fs\proc\proc_misc.c
文件的proc_misc_init()函数中,

搜寻”kmsg”,找到位于fs\proc\proc_misc.c
文件的proc_misc_init()函数中,

该函数首要用来生成登记的装置文件,具体代码如下所示:

该函数紧要用来生成登记的设施文件,具体代码如下所示:

const struct file_operations proc_kmsg_operations = {
       .read              = kmsg_read,               //读函数
       .poll        = kmsg_poll,
       .open             = kmsg_open,
       .release   = kmsg_release,
};


void __init proc_misc_init(void)
{
  ... ...
  struct proc_dir_entry *entry;                                   // 用来描述文件的结构体,
         entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); //使用create_proc_entry()创建文件
if (entry)
         entry->proc_fops = &proc_kmsg_operations;    //对创建的文件赋入file_ operations
    ... ...
}
const struct file_operations proc_kmsg_operations = {
       .read              = kmsg_read,               //读函数
       .poll        = kmsg_poll,
       .open             = kmsg_open,
       .release   = kmsg_release,
};


void __init proc_misc_init(void)
{
  ... ...
  struct proc_dir_entry *entry;                                   // 用来描述文件的结构体,
         entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); //使用create_proc_entry()创建文件
if (entry)
         entry->proc_fops = &proc_kmsg_operations;    //对创建的文件赋入file_ operations
    ... ...
}

从地点代码得出,/proc/kmsg文件,也是有file_operations结构体的,而cat命令就会直接读/proc/kmsg的file_operations->read(),实现读log_buf[]的数据

从地方代码得出,/proc/kmsg文件,也是有file_operations结构体的,而cat命令就会间接读/proc/kmsg的file_operations->read(),实现读log_buf[]的数据

且/proc/kmsg文件是透过create_proc_entry()创立出来的,参数如下所示:

且/proc/kmsg文件是经过create_proc_entry()创建出来的,参数如下所示:

“kmsg”:文件名

“kmsg”:文件名

&proc_root:父目录,表示存在/proc根目录下

&proc_root:父目录,表示存在/proc根目录下

S_IRUSR: 等于400,表示拥有者(usr)可读,其他任何人不可以开展其余操作,如下图所示:

S_IRUSR: 等于400,表示拥有者(usr)可读,其他任谁无法开展此外操作,如下图所示:

 ca88亚洲城网站 14

 ca88亚洲城网站 15

该参数和chmod命令参数一样,除了S_IRUSR还有不少参数,比如:

该参数和chmod命令参数一样,除了S_IRUSR还有好多参数,比如:

S_IRWXU: 等于700, 表示拥有者(usr)可读(r)可写(w)可举行(x)

S_IRWXU: 等于700, 表示拥有者(usr)可读(r)可写(w)可实施(x)

S_IRWXG: 等于070, 表示拥有者和组用户 (group)可读(r)可写(w)可进行(x)

S_IRWXG: 等于070, 表示拥有者和组用户 (group)可读(r)可写(w)可举行(x)

 

 

4.为何使用dmesg命令和cat
/proc/kmsg会有这么大的分别?

4.为何使用dmesg命令和cat
/proc/kmsg会有这般大的区别?

俺们进去proc_kmsg_operations-> kmsg_read()看看,就知晓了

我们进来proc_kmsg_operations-> kmsg_read()看看,就精晓了

static ssize_t kmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
       /*若在非阻塞访问,且没有读的数据,则立刻return*/
       if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
              return -EAGAIN;
       return do_syslog(2, buf, count);          //开始读数据,buf:用户层地址,count:要读的数据长度
}
static ssize_t kmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
       /*若在非阻塞访问,且没有读的数据,则立刻return*/
       if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
              return -EAGAIN;
       return do_syslog(2, buf, count);          //开始读数据,buf:用户层地址,count:要读的数据长度
}

5.proc_kmsg_operations->
kmsg_read()->**
do_syslog(9, NULL,
0)**的情节如下所示:

5.proc_kmsg_operations->
kmsg_read()->**
do_syslog(9, NULL,
0)**的始末如下所示:

 ca88亚洲城网站 16

 ca88亚洲城网站 17

其中log_start和log_end就是环形缓冲区的六个标志,
log_start也可以称为读标志位,
log_end也足以称作写标志位,当写标志和读标志一致时,则代表尚未读的数码了。

其中log_start和log_end就是环形缓冲区的几个标志,
log_start也得以称为读标志位,
log_end也可以称之为写标志位,当写标志和读标志一致时,则意味从未读的数码了。

6.proc_kmsg_operations->
kmsg_read()->**
do_syslog(2, buf,
count)**的内容如下所示:

6.proc_kmsg_operations->
kmsg_read()->**
do_syslog(2, buf,
count)**的情节如下所示:

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)) {      // access_ok:检查用户层地址是否访问OK
                     error = -EFAULT;
                     goto out;
              }

              /*若没有读的数据,则进入等待队列*/
              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_BUF:取环形缓冲区log_buf[]里的某个位置的数据
                     log_start++;                        //读地址++
                     spin_unlock_irq(&logbuf_lock);
                     error = __put_user(c,buf);            //和 copy_to_user()函数一样,都是上传用户数据
                     buf++;                                       //用户地址++
                     i++;                                        //读数据长度++
                     cond_resched();
                     spin_lock_irq(&logbuf_lock);
              }
              spin_unlock_irq(&logbuf_lock);
              if (!error)
                     error = i;
              break;}
out:
       return error;
}
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)) {      // access_ok:检查用户层地址是否访问OK
                     error = -EFAULT;
                     goto out;
              }

              /*若没有读的数据,则进入等待队列*/
              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_BUF:取环形缓冲区log_buf[]里的某个位置的数据
                     log_start++;                        //读地址++
                     spin_unlock_irq(&logbuf_lock);
                     error = __put_user(c,buf);            //和 copy_to_user()函数一样,都是上传用户数据
                     buf++;                                       //用户地址++
                     i++;                                        //读数据长度++
                     cond_resched();
                     spin_lock_irq(&logbuf_lock);
              }
              spin_unlock_irq(&logbuf_lock);
              if (!error)
                     error = i;
              break;}
out:
       return error;
}

分明就是对环形缓冲区的读操作,而环形缓冲区的法则又是何许?

明显就是对环形缓冲区的读操作,而环形缓冲区的法则又是哪些?

7.接下来便来分析环形缓冲区的规律

7.接下来便来分析环形缓冲区的原理

和下面函数一样, 环形缓冲区需要一个大局数组,还亟需三个标志:读标志R、写标志W

和上边函数一样, 环形缓冲区需要一个大局数组,还亟需多少个标志:读标志R、写标志W

咱俩以一个大局数组my_buff[7]为例,来分析:

我们以一个大局数组my_buff[7]为例,来分析:

7.1环形缓冲区初步时:

7.1环形缓冲区伊始时:

int R=0;             //记录读的位置
int W=0;             //记录写的位置    
int R=0;             //记录读的位置
int W=0;             //记录写的位置    

地点的代码,如下图1所示:

上边的代码,如下图1所示:

 ca88亚洲城网站 18

 ca88亚洲城网站 19

 

 

R:从数组[R]开始读数据

R:从数组[R]发端读数据

W:从数组[W]起初写多少

W:从数组[W]起来写多少

之所以,当R==W时,则意味尚无数量可读,通过那么些逻辑便能写出读数据了

因此,当R==W时,则代表没有多少可读,通过那些逻辑便能写出读数据了

7.2当咱们需要读数据时:

7.2当我们需要读数据时:

int read_buff(char  *p)              //p:指向要读出的地址
{
if(R==W)            
      return 0;       //读失败
*p=my_buff[R];
R=(R+1)%7;      //R++    
return  1;         //读成功  
}
int read_buff(char  *p)              //p:指向要读出的地址
{
if(R==W)            
      return 0;       //读失败
*p=my_buff[R];
R=(R+1)%7;      //R++    
return  1;         //读成功  
}

我们以W=3,R=0,为例,调用3次read_buff()**函数,**如下图所示:

我们以W=3,R=0,为例,调用3次read_buff()**函数,**如下图所示:

 ca88亚洲城网站 20

 ca88亚洲城网站 21

 

 

读数据完成,剩下就是写多少了,很分明每写一个数额,W则++

读数据完成,剩下就是写多少了,很了然每写一个数量,W则++

7.3所以写多少函数为:

7.3所以写多少函数为:

void write_buff(char c)              //c:等于要写入的内容
{
  my_buff [W]=c;         
  W=(W+1)%7;    //W++          
if(W==R)
  R=(R+1)%7;      //R++
}
void write_buff(char c)              //c:等于要写入的内容
{
  my_buff [W]=c;         
  W=(W+1)%7;    //W++          
if(W==R)
  R=(R+1)%7;      //R++
}

7.3.1
下边的代码,为何要判断if((W==R)?

7.3.1
下边的代码,为啥要咬定if((W==R)?

诸如,当我们写入一个8个数据,而my_buff[]只好存7个数据,必定会有W==R的时候,倘使不加该论断,效能图如下所示:

诸如,当我们写入一个8个数据,而my_buff[]不得不存7个数据,必定会有W==R的时候,假若不加该论断,效能图如下所示:

 ca88亚洲城网站 22

 ca88亚洲城网站 23

 

 

下一场我们再反复调用read_buff(),就会意识只读的出第8个数据的值,而眼前7个数据都会被摒弃掉

然后大家再频繁调用read_buff(),就会意识只读的出第8个数据的值,而眼前7个数据都会被摒弃掉

7.3.2而进入判断后,效能图如下所示:

7.3.2而进入判断后,效用图如下所示:

 ca88亚洲城网站 24

 ca88亚洲城网站 25

 

 

下一场大家再频繁调用read_buff(),就足以读出my_buff [2]~ my_buff
[0]共6个数据出来

下一场我们再频繁调用read_buff(),就足以读出my_buff [2]~ my_buff
[0]共6个数据出来

总结:

总结:

由于read_buff()后,R都会+1,所以每一遍 cat /proc/kmsg ,
都会清空上次的打印音讯。

由于read_buff()后,R都会+1,所以每一回 cat /proc/kmsg ,
都会清空上次的打印信息。

 

 

8.环形缓冲区分析完毕后,大家就足以一直来写一个驱动,模仿/proc/kmsg文件来探望

8.环形缓冲区分析完毕后,大家就可以直接来写一个驱动,模仿/proc/kmsg文件来探望

流程如下:

流程如下:

  • 1)概念全局数组my_buff[1000]环形缓冲区,R标志,W标明,然后提供写函数,读函数
  • 2)自制一个myprintk(),通过传播的多少来放入到my_buff[]环形缓冲区中
  • (PS:需要通过EXPORT_SYMBOL(myprintk)表明该myprintk,否则不能够被其他驱动程序调用
    )
  • 3)写入口函数
  •    ->3.1) 通过create_proc_entry()创建/proc/mykmsg文件
  •  
     ->3.2
     )并向mykmsg文件里添加file_operations结构体
  • 4)写出口函数
  •  
     ->4.1)
    通过remove_proc_entry()卸载/proc/mykmsg文件
  • 5)file_operations->read()函数
  •    ->5.1) 仿照/proc/kmsg的read()函数,来读my_buff[]环形缓冲区的数据
  • 1)概念全局数组my_buff[1000]环形缓冲区,R标志,W标志,然后提供写函数,读函数
  • 2)自制一个myprintk(),通过传播的多少来放入到my_buff[]环形缓冲区中
  • (PS:需要通过EXPORT_SYMBOL(myprintk)表明该myprintk,否则无法被另外驱动程序调用
    )
  • 3)写入口函数
  •    ->3.1) 通过create_proc_entry()创建/proc/mykmsg文件
  •  
     ->3.2
     )并向mykmsg文件里添加file_operations结构体
  • 4)写出口函数
  •  
     ->4.1)
    通过remove_proc_entry()卸载/proc/mykmsg文件
  • 5)file_operations->read()函数
  •    ->5.1) 仿照/proc/kmsg的read()函数,来读my_buff[]环形缓冲区的数据

具体代码如下所示:

切切实实代码如下所示:

#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 my_buff_len   1000          //环形缓冲区长度

static struct proc_dir_entry *my_entry;


/*    声明等待队列类型中断 mybuff_wait      */
static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);


static char my_buff[my_buff_len]; 
unsigned long R=0;                      //记录读的位置
unsigned long W=0;                    //记录写的位置

int read_buff(char  *p)         //p:指向要读出的地址
{
   if(R==W)          
             return 0;               //读失败
        *p=my_buff[R]; 
         R=(R+1)%my_buff_len;       //R++
        return  1;                   //读成功   
}

void write_buff(char c)          //c:等于要写入的内容
{    
        my_buff [W]=c;       
        W=(W+1)%my_buff_len;     //W++
        if(W==R)
            R=(R+1)%my_buff_len;     //R++
       wake_up_interruptible(&mybuff_wait);     //唤醒队列,因为R != W 
}

/*打印到my_buff[]环形缓冲区中*/
int myprintk(const char *fmt, ...)
{
       va_list args;
       int i,len;
       static char temporary_buff[my_buff_len];        //临时缓冲区
       va_start(args, fmt);
       len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
       va_end(args);

        /*将临时缓冲区放入环形缓冲区中*/
       for(i=0;i<len;i++)       
       {
            write_buff(temporary_buff[i]);
       }
       return len;
}

static int mykmsg_open(struct inode *inode, struct file *file)
{
        return 0;
}  

static int mykmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
      int error = 0,i=0;
      char c;

        if((file->f_flags&O_NONBLOCK)&&(R==W))      //非阻塞情况下,且没有数据可读
            return  -EAGAIN;

       error = -EINVAL;

              if (!buf || !count )
                     goto out;

       error = wait_event_interruptible(mybuff_wait,(W!=R));
              if (error)
                     goto out;

       while (!error && (read_buff(&c)) && i < count) 
      {
        error = __put_user(c,buf);      //上传用户数据
        buf ++;
        i++;
      }

      if (!error)
               error = i;
out:
       return error;
}  

const struct file_operations mykmsg_ops = {
       .read              = mykmsg_read,
       .open         = mykmsg_open,
};
static int  mykmsg_init(void)
{
    my_entry = create_proc_entry("mykmsg", S_IRUSR, &proc_root);
   if (my_entry)
         my_entry->proc_fops = &mykmsg_ops;
    return 0;
}
static void mykmsg_exit(void)
{
        remove_proc_entry("mykmsg", &proc_root);  
}

module_init(mykmsg_init);
module_exit(mykmsg_exit); 
EXPORT_SYMBOL(myprintk);
MODULE_LICENSE("GPL");
#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 my_buff_len   1000          //环形缓冲区长度

static struct proc_dir_entry *my_entry;


/*    声明等待队列类型中断 mybuff_wait      */
static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);


static char my_buff[my_buff_len]; 
unsigned long R=0;                      //记录读的位置
unsigned long W=0;                    //记录写的位置

int read_buff(char  *p)         //p:指向要读出的地址
{
   if(R==W)          
             return 0;               //读失败
        *p=my_buff[R]; 
         R=(R+1)%my_buff_len;       //R++
        return  1;                   //读成功   
}

void write_buff(char c)          //c:等于要写入的内容
{    
        my_buff [W]=c;       
        W=(W+1)%my_buff_len;     //W++
        if(W==R)
            R=(R+1)%my_buff_len;     //R++
       wake_up_interruptible(&mybuff_wait);     //唤醒队列,因为R != W 
}

/*打印到my_buff[]环形缓冲区中*/
int myprintk(const char *fmt, ...)
{
       va_list args;
       int i,len;
       static char temporary_buff[my_buff_len];        //临时缓冲区
       va_start(args, fmt);
       len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
       va_end(args);

        /*将临时缓冲区放入环形缓冲区中*/
       for(i=0;i<len;i++)       
       {
            write_buff(temporary_buff[i]);
       }
       return len;
}

static int mykmsg_open(struct inode *inode, struct file *file)
{
        return 0;
}  

static int mykmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
      int error = 0,i=0;
      char c;

        if((file->f_flags&O_NONBLOCK)&&(R==W))      //非阻塞情况下,且没有数据可读
            return  -EAGAIN;

       error = -EINVAL;

              if (!buf || !count )
                     goto out;

       error = wait_event_interruptible(mybuff_wait,(W!=R));
              if (error)
                     goto out;

       while (!error && (read_buff(&c)) && i < count) 
      {
        error = __put_user(c,buf);      //上传用户数据
        buf ++;
        i++;
      }

      if (!error)
               error = i;
out:
       return error;
}  

const struct file_operations mykmsg_ops = {
       .read              = mykmsg_read,
       .open         = mykmsg_open,
};
static int  mykmsg_init(void)
{
    my_entry = create_proc_entry("mykmsg", S_IRUSR, &proc_root);
   if (my_entry)
         my_entry->proc_fops = &mykmsg_ops;
    return 0;
}
static void mykmsg_exit(void)
{
        remove_proc_entry("mykmsg", &proc_root);  
}

module_init(mykmsg_init);
module_exit(mykmsg_exit); 
EXPORT_SYMBOL(myprintk);
MODULE_LICENSE("GPL");

 

 

PS:当其他驱动向利用myprintk()打印函数,还亟需在文件中扬言,才行:

PS:当其他驱动向利用myprintk()打印函数,还亟需在文件中宣称,才行:

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

且还亟需先装载mykmsg驱动,再来装载要采纳myprintk()的驱动,否则不可能找到myprintk()函数

且还需要先装载mykmsg驱动,再来装载要使用myprintk()的驱动,否则无法找到myprintk()函数

 

 

9.测试运行

9.测试运行

一般来说图所示,挂载了mykmsg驱动,可以看看变化了一个/proc/mykmsg文件

一般来说图所示,挂载了mykmsg驱动,能够见到变化了一个/proc/mykmsg文件

 ca88亚洲城网站 26

 ca88亚洲城网站 27

挂载/proc/mykmsg期间,其它驱动使用myprintk()函数,就会将音讯打印在/proc/mykmsg文件中,如下图所示:

挂载/proc/mykmsg期间,其余驱动使用myprintk()函数,就会将信息打印在/proc/mykmsg文件中,如下图所示:

  ca88亚洲城网站 28

  ca88亚洲城网站 29

和cat /proc/kmsg一样,每一回cat
都会清上五次的打印数据

和cat /proc/kmsg一样,每趟cat
都会清上五遍的打印数据

10.若大家不想每一回清,和dmesg命令一样,
老是都能打印出环形缓冲区的有所消息,该咋样改mykmsg驱动?

10.若我们不想每一趟清,和dmesg命令一样,
历次都能打印出环形缓冲区的保有音信,该怎么改mykmsg驱动?

上次我们解析过了,每一遍调用read_buff()后,R都会+1。

上次大家解析过了,每一趟调用read_buff()后,R都会+1。

要想不清空上次的消息打印,还亟需定义一个R_ current标志来代替R标志,这样每便cat为止后,R的职务保持不变。

要想不清空上次的音信打印,还索要定义一个R_ current标志来代替R标志,这样每回cat停止后,R的职位保持不变。

老是cat时,系统除了进入file_operations->
read(),还会进入file_operations-> open(),所以在open()里,使R_
current=R,然后在改动部分代码即可,

老是cat时,系统除去进入file_operations->
read(),还会进来file_operations-> open(),所以在open()里,使R_
current=R,然后在修改部分代码即可,

10.1我们依然以一个大局数组my_buff[7]为例,
正如图所示:

10.1我们依旧以一个大局数组my_buff[7]为例,
正如图所示:

ca88亚洲城网站 30

ca88亚洲城网站 31

10.2所以,修改的代码如下所示:

10.2据此,修改的代码如下所示:

#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 my_buff_len   1000          //环形缓冲区长度

static struct proc_dir_entry *my_entry;

/*    声明等待队列类型中断 mybuff_wait      */
static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);

static char my_buff[my_buff_len];

unsigned long R=0;                      //记录读的位置
unsigned long R_current=0;             //记录cat期间 读的位置
unsigned long W=0;                    //记录写的位置

int read_buff(char  *p)         //p:指向要读出的地址
{
        if(R_current==W)             
             return 0;               //读失败
        *p=my_buff[R_current]; 
         R_current=(R_current+1)%my_buff_len;     //R_current++
        return  1;                   //读成功   
}

void write_buff(char c)          //c:等于要写入的内容
{    
        my_buff [W]=c;       
        W=(W+1)%my_buff_len;     //W++
        if(W==R)
          R=(R+1)%my_buff_len;     //R++
        if(W==R_current)
          R=(R+1)%my_buff_len;     //R_current++
       wake_up_interruptible(&mybuff_wait);     //唤醒队列,因为R !=W 
}

/*打印到my_buff[]环形缓冲区中*/
int myprintk(const char *fmt, ...)
{
  va_list args;
  int i,len;
  static char temporary_buff[my_buff_len];        //临时缓冲区
  va_start(args, fmt);
  len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
  va_end(args);
     /*将临时缓冲区放入环形缓冲区中*/
       for(i=0;i<len;i++)       
       {
            write_buff(temporary_buff[i]);
       }
  return len;
}

static int mykmsg_open(struct inode *inode, struct file *file)
{
        R_current=R;       
        return 0;
}  

static int mykmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
      int error = 0,i=0;
      char c;     
        if((file->f_flags&O_NONBLOCK)&&(R_current==W))      //非阻塞情况下,且没有数据可读
            return  -EAGAIN;
        error = -EINVAL;
       if (!buf || !count )
              goto out;

       error = wait_event_interruptible(mybuff_wait,(W!=R_current));
       if (error)
              goto out;

      while (!error && (read_buff(&c)) && i < count) 
      {
        error = __put_user(c,buf);      //上传用户数据
        buf ++;
        i++;
      }

      if (!error)
        error = i;

out:
  return error;
}  

const struct file_operations mykmsg_ops = {
  .read         = mykmsg_read,
  .open         = mykmsg_open,

};

static int  mykmsg_init(void)
{
    my_entry = create_proc_entry("mykmsg", S_IRUSR, &proc_root);
   if (my_entry)
    my_entry->proc_fops = &mykmsg_ops;
    return 0;
}
static void mykmsg_exit(void)
{
   remove_proc_entry("mykmsg", &proc_root);
}

module_init(mykmsg_init);
module_exit(mykmsg_exit); 
EXPORT_SYMBOL(myprintk); 
MODULE_LICENSE("GPL"); 
#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 my_buff_len   1000          //环形缓冲区长度

static struct proc_dir_entry *my_entry;

/*    声明等待队列类型中断 mybuff_wait      */
static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);

static char my_buff[my_buff_len];

unsigned long R=0;                      //记录读的位置
unsigned long R_current=0;             //记录cat期间 读的位置
unsigned long W=0;                    //记录写的位置

int read_buff(char  *p)         //p:指向要读出的地址
{
        if(R_current==W)             
             return 0;               //读失败
        *p=my_buff[R_current]; 
         R_current=(R_current+1)%my_buff_len;     //R_current++
        return  1;                   //读成功   
}

void write_buff(char c)          //c:等于要写入的内容
{    
        my_buff [W]=c;       
        W=(W+1)%my_buff_len;     //W++
        if(W==R)
          R=(R+1)%my_buff_len;     //R++
        if(W==R_current)
          R=(R+1)%my_buff_len;     //R_current++
       wake_up_interruptible(&mybuff_wait);     //唤醒队列,因为R !=W 
}

/*打印到my_buff[]环形缓冲区中*/
int myprintk(const char *fmt, ...)
{
  va_list args;
  int i,len;
  static char temporary_buff[my_buff_len];        //临时缓冲区
  va_start(args, fmt);
  len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
  va_end(args);
     /*将临时缓冲区放入环形缓冲区中*/
       for(i=0;i<len;i++)       
       {
            write_buff(temporary_buff[i]);
       }
  return len;
}

static int mykmsg_open(struct inode *inode, struct file *file)
{
        R_current=R;       
        return 0;
}  

static int mykmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
      int error = 0,i=0;
      char c;     
        if((file->f_flags&O_NONBLOCK)&&(R_current==W))      //非阻塞情况下,且没有数据可读
            return  -EAGAIN;
        error = -EINVAL;
       if (!buf || !count )
              goto out;

       error = wait_event_interruptible(mybuff_wait,(W!=R_current));
       if (error)
              goto out;

      while (!error && (read_buff(&c)) && i < count) 
      {
        error = __put_user(c,buf);      //上传用户数据
        buf ++;
        i++;
      }

      if (!error)
        error = i;

out:
  return error;
}  

const struct file_operations mykmsg_ops = {
  .read         = mykmsg_read,
  .open         = mykmsg_open,

};

static int  mykmsg_init(void)
{
    my_entry = create_proc_entry("mykmsg", S_IRUSR, &proc_root);
   if (my_entry)
    my_entry->proc_fops = &mykmsg_ops;
    return 0;
}
static void mykmsg_exit(void)
{
   remove_proc_entry("mykmsg", &proc_root);
}

module_init(mykmsg_init);
module_exit(mykmsg_exit); 
EXPORT_SYMBOL(myprintk); 
MODULE_LICENSE("GPL"); 

 

 

11.测试运行

11.测试运行

 ca88亚洲城网站 32

 ca88亚洲城网站 33

 

 

 

 

 

 

相关文章