1. Ring buffer log_buf[] Which file exists in the kernel ?

be located /proc/kmsg in , So in addition to dmesg Command view , You can also use cat /proc/kmsg Check it out.

2. however ,dmesg Command and cat /proc/kmsg Somewhat different

2.1 dmesg command

Each use , Will print out all the information in the ring buffer

2.2 cat /proc/kmsg

Only the information of each new ring buffer is printed out

such as , For the first time cat /proc/kmsg, Will print out all the information about the kernel boot

Use... For the second time cat /proc/kmsg, The information printed before will not appear , Only print after last use cat /proc/kmsg New information after , See the image below :
 Insert picture description here

3. And then we go to the kernel , look for /proc/kmsg Where was the file generated

Search for "kmsg", Found in the fs\proc\proc_misc.c Of documents proc_misc_init() Function ,

This function is mainly used to generate registered device files , The specific code is as follows :

const struct file_operations proc_kmsg_operations = {   .read              = kmsg_read,               // Read function    .poll        = kmsg_poll,   .open             = kmsg_open,   .release   = kmsg_release,};
 void __init proc_misc_init(void){
  ... ...
  struct proc_dir_entry *entry;                                   //  The structure used to describe a file , entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); // Use create_proc_entry() create a file if (entry) entry->proc_fops = &proc_kmsg_operations;    // Assign... To the created file file_ operations... ...}

From the code above ,/proc/kmsg file , There is a file_operations Structure of the , and cat The command will be read all the time /proc/kmsg Of file_operations->read(), Realize reading log_buf[] The data of

And /proc/kmsg The document is passed create_proc_entry() created , The parameters are as follows :

kmsg”: file name

&proc_root: Parent directory , Indicates presence /proc The root directory

S_IRUSR: be equal to 400, Means the owner (usr) Can be read , No one else can do anything , As shown in the figure below :
 Insert picture description here
The parameters and chmod The command parameters are the same , except S_IRUSR There are many parameters , such as :

S_IRWXU: be equal to 700, Means the owner (usr) Can read but write (w) Executable (x)

S_IRWXG: be equal to 070, Represents the owner and group user (group) Can read but write (w) Executable (x)

4. Why use dmesg Command and cat /proc/kmsg There will be such a big difference ?

We enter proc_kmsg_operations-> kmsg_read() have a look , You know the

static ssize_t kmsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos){   /* If in non blocking access , And there's no read data , Then immediately return*/   if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))  return -EAGAIN;   return do_syslog(2, buf, count);          // Start reading data ,buf: User layer address ,count: The length of data to read }

5.proc_kmsg_operations-> kmsg_read()->do_syslog(9, NULL, 0) The contents of are as follows :

among log_start and log_end These are the two flags of the ring buffer , log_start It can also be called read flag bit , log_end It can also be called write flag bit , When the write flag is consistent with the read flag , There is no data to read .

6.proc_kmsg_operations-> kmsg_read()->do_syslog(2, buf, count) The contents of are as follows :

case 2:           /* Read from log */  error = -EINVAL;  if (!buf || len < 0)           // Judge whether the user layer is empty , And read data length  goto out;  error = 0;  if (!len) goto out;  if (!access_ok(VERIFY_WRITE, buf, len)) {      // access_ok: Check whether the user layer address is accessed OK error = -EFAULT; goto out;  }  /* If there is no data to read , Then enter the waiting line */  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: Take the ring buffer log_buf[] Data from a location in the  log_start++;                        // Read the address ++ spin_unlock_irq(&logbuf_lock); error = __put_user(c,buf);            // and  copy_to_user() The function is the same , It's all about uploading user data  buf++;                                       // Address of the user ++ i++;                                        // Read data length ++ cond_resched(); spin_lock_irq(&logbuf_lock);  }  spin_unlock_irq(&logbuf_lock);  if (!error) error = i;  break;}out:   return error;}

It's obviously a read to the ring buffer , What is the principle of ring buffer ?

####7. Next, we will analyze the principle of ring buffer

Just like the above function , The ring buffer needs a global array , Two more logos are needed : Read the sign R、 Write a sign W

Let's take a global array my_buff[7] For example , To analyze :

7.1 At the beginning of the ring buffer :

int R=0;             // Record the location of the read int W=0;             // Where is the record written 

The above code , Here's the picture 1 Shown :
 Insert picture description here
R: From an array [R] Start reading data

W: From an array [W] Start writing data

therefore , When R==W when , There is no data to read , Through this logic, we can write the read data

7.2 When we need to read data :

int read_buff(char  *p)              //p: Point to the address to be read {if(R==W)            
      return 0;       // Read failed *p=my_buff[R];R=(R+1)%7;      //R++    return  1;         // Read successfully   }

We use W=3,R=0, For example , call 3 Time read_buff() function , As shown in the figure below :
 Insert picture description here
Read data complete , The rest is to write data , Obviously, every time you write a piece of data ,W be ++

7.3 So the write data function is :

void write_buff(char c)              //c: It's equal to what you want to write {
  my_buff [W]=c;         
  W=(W+1)%7;    //W++          if(W==R)
  R=(R+1)%7;      //R++}

7.3.1 The above code , Why judge if((W==R)?

such as , When we write a 8 Data , and my_buff[] Can only save 7 Data , There must be W==R When , If you don't judge , The effect is shown below :
 Insert picture description here
And then we call it many times read_buff(), You'll find the read-only output 8 Value of data , And the front 7 All data will be discarded

7.3.2 And with judgment , The effect is shown below :

 Insert picture description here
And then we call it many times read_buff(), You can read my_buff [2]~ my_buff [0] common 6 There's a number out there

summary :

because read_buff() after ,R Metropolis +1, So every time cat /proc/kmsg , Will clear the last print information .

8. After analyzing the ring buffer , We can write a driver directly , imitation /proc/kmsg Let's take a look at the documents

The process is as follows :

1) Define global array my_buff[1000] Ring buffer ,R sign ,W sign , And then provide a write function , Read function
2) Make one by yourself myprintk(), Put the data into my_buff[] In the ring buffer
(PS: Need to pass through EXPORT_SYMBOL(myprintk) Declare that myprintk, Otherwise, it cannot be called by other drivers )
3) Write entry function
->3.1) adopt create_proc_entry() establish /proc/mykmsg file
->3.2 ) And to mykmsg Add to file file_operations Structure
4) Write the exit function
->4.1) adopt remove_proc_entry() uninstall /proc/mykmsg file
5) Write file_operations->read() function
->5.1) Modelled on the /proc/kmsg Of read() function , To read my_buff[] Data in the ring buffer
The specific code is as follows :

#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          // Ring buffer length static struct proc_dir_entry *my_entry;/*     Declare wait queue type interrupt  mybuff_wait      */static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);static char my_buff[my_buff_len]; unsigned long R=0;                      // Record the location of the read unsigned long W=0;                    // Where is the record written
 int read_buff(char  *p)         //p: Point to the address to be read {
   if(R==W)          
             return 0;               // Read failed *p=my_buff[R]; 
         R=(R+1)%my_buff_len;       //R++return  1;                   // Read successfully    }void write_buff(char c)          //c: It's equal to what you want to write {    
        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);     // Wake up the queue , because R != W }/* print to my_buff[] In the ring buffer */int myprintk(const char *fmt, ...){   va_list args;   int i,len;   static char temporary_buff[my_buff_len];        // Temporary buffer    va_start(args, fmt);   len=vsnprintf(temporary_buff, INT_MAX, fmt, args);   va_end(args);/* Put the temporary buffer in the ring buffer */   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))      // In the non blocking case , And no data to read 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);      // Upload user data 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: When other drivers use myprintk() Print function , It also needs to be declared in the file , Talent :

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

And it needs to be loaded first mykmsg drive , And then we're going to use myprintk() The driver , Otherwise, we can't find myprintk() function

9. test run

As shown in the figure below , Mounted mykmsg drive , You can see that a /proc/mykmsg file
 Insert picture description here
mount /proc/mykmsg period , Other drivers use myprintk() function , Will print the information on /proc/mykmsg In file , As shown in the figure below :
 Insert picture description here
and cat /proc/kmsg equally , Every time cat Will clear the last print data

10. If we don't want to clear every time , and dmesg command , All the information in the ring buffer can be printed every time , How to change mykmsg drive ?

Last time we analyzed , Every time you call read_buff() after ,R Metropolis +1.

If you want to print without emptying the last information , You also need to define a R_ current Sign instead of R sign , So every time cat After the end ,R Keep the position of .

Every time cat when , In addition to entering file_operations-> read(), And into file_operations-> open(), So in open() in , send R_ current=R, Then modify part of the code ,

10.1 Let's take a global array my_buff[7] For example , As shown in the figure below :

 Insert picture description here

10.2 therefore , The modified code is as follows :

#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          // Ring buffer length
 static struct proc_dir_entry *my_entry;
 /*     Declare wait queue type interrupt  mybuff_wait      */static DECLARE_WAIT_QUEUE_HEAD(mybuff_wait);static char my_buff[my_buff_len];unsigned long R=0;                      // Record the location of the read unsigned long R_current=0;             // Record cat period   Where to read unsigned long W=0;                    // Where is the record written int read_buff(char  *p)         //p: Point to the address to be read {if(R_current==W)             
             return 0;               // Read failed *p=my_buff[R_current]; 
         R_current=(R_current+1)%my_buff_len;     //R_current++return  1;                   // Read successfully    }void write_buff(char c)          //c: It's equal to what you want to write {    
        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);     // Wake up the queue , because R !=W }/* print to my_buff[] In the ring buffer */int myprintk(const char *fmt, ...){
  va_list args;
  int i,len;
  static char temporary_buff[my_buff_len];        // Temporary buffer
  va_start(args, fmt);
  len=vsnprintf(temporary_buff, INT_MAX, fmt, args);
  va_end(args); /* Put the temporary buffer in the ring buffer */   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))      // In the non blocking case , And no data to read 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);      // Upload user data 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. test run

 Insert picture description here