Interface between kernel proc file system and seq -- seq_ Analysis of file interface programming

Since the default operation function of procfs only uses one page cache, it is a little troublesome when processing large proc files, and it is not flexible when outputting data in a series of structures. You need to read it yourself_ The implementation of iteration in proc function is prone to bugs. So the kernel hackers studied some / proc codes, abstracted commonalities, and finally formed seq_file (Sequence file) interface. This interface provides a set of simple functions to solve the problems existing in the above proc interface programming, making programming easier and reducing the chance of bugs.

When you need to create a virtual file composed of a series of data or a large virtual file, SEQ is recommended_ File interface. But I personally think that procfs is not the only one who can use this seq_file interface, because actually seq_file is a set of operation functions. This set of functions is not bound with proc, and can also be used in other places. For the study of a function interface layer, we should first look at a related data structure, struct seq_file:

include/linux/seq_file.h

struct seq_file {
char *buf;    		//seq_ Cache page pointer used by file interface
size_t size;  		//seq_ The cache page size used by the file interface
size_t from;  		//From seq_ Offset address relative to buf when copying to user state buffer in file
size_t count; 		//The number of characters in buf that can be copied to user status
loff_t index; 		//Subscript pos value of start and next processing
loff_t read_pos; 	//The amount of data that has been copied to the user status
u64 version;
struct mutex lock; 		//For this seq_ Mutex of file operation, all seq_* All access will be locked
const struct seq_operations *op; //Functions that manipulate actual underlying data
void *private;
};

In this structure, almost all members are composed of seq_file internal implementation to deal with, programmers do not need to care about, unless you want to study SEQ_ The internal principle of file. For this structure, the only thing programmers need to do is to implement const struct seq_operations *op. Use SEQ for_ The file interface accesses different data structures. You must create a set of simple object iterative operation functions.

struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};

  seq_ The internal mechanism of file uses these interface functions to access the actual data structure at the bottom, and continues to move forward along the data sequence, while outputting the data in the sequence to SEQ one by one_ File is in the self built cache (one page in size). That is to say, seq_ The internal mechanism of file helps you realize the mechanism of reading and caching sequence data. You only need to implement the underlying iterative function interface, because these are related to the underlying data you want to access, and seq_file belongs to the upper abstraction. This may seem a little complicated. You can understand it by looking at the following figure:

 

Here we will briefly introduce struct SEQ_ Functions in operations:

  void * (*start) (struct seq_file *m, loff_t *pos);

  1. The start method will be called first. Its function is to set the starting point of access.
  2. m: It points to this seq_file structure, which does not need to be processed under normal circumstances.
  3. pos: is an integer position value indicating where to start reading. The meaning of this location depends entirely on the underlying implementation, not necessarily the location in bytes, but the sequence number of an element.
  4. If the return value is not NULL, it is a pointer to the private data structure implemented by the iterator. NULL if access error.

 

    

Set the access starting point, seq_ The internal mechanism of file may use the show method to obtain the data in the structure pointed to by the return value of start to the internal cache and send it to the user space in time.

  1. int (*show) (struct seq_file *m, void *v);

Therefore, the show method is responsible for outputting the data in the v pointing element to seq_file, but it must use seq_file provides some interface functions similar to printf:

int seq_printf(struct seq_file *sfile, const char *fmt, ...);
//Designed for seq_file implements a function similar to printf; Used to convert data into common format strings and added value parameters
//You must give the set to the show function_ The file structure pointer is passed to it. If seq_printf returns - 1, which means that the cache is full and part of the output is discarded. But most of the time, its return value is ignored.

int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);
//Similar to putc and puts functions, sfile parameters and return values are the same as SEQ_ Same as printf.
int seq_escape(struct seq_file *m, const char *s, const char *esc);
//This function is similar to seq_puts, but it will output all characters in s that appear in esc to the cache in octal format.
//The common value of esc is "\ t\n \ \", which makes the embedded space not confuse the output or confuse the shell script

  int seq_write(struct seq_file *seq, const void *data, size_t len) 
  //Directly write the data pointed to by data to seq_file cache, the data length is len. Used for non string data.

int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry *dentry, char *esc);
//This function can be used to output the file name associated with a given directory item, which is rarely used by the driver.  

After the show function returns, seq_ The file mechanism may need to move to the next data element, so the next method must be used.

void * (*next) (struct seq_file *m, void *v, loff_t *pos);

  1. v: Is the element pointer returned by the previous call to start or next. It may be the element pointed to by the completed output of the previous show.
  2. pos: the index value of the element to be moved to.


In the next implementation, the value pointed to by pos should be incremented, but the specific amount of increment is related to the implementation of iterators, not necessarily 1. If the return value of next is not NULL, it is the next element pointer that needs to be output to the cache. Otherwise, it indicates that the output has ended, and the stop method will be called for cleaning.

 

  1. void (*stop) (struct seq_file *m, void *v);

 

In the stop implementation, the parameter m points to this seq_file structure, which does not need to be processed under normal circumstances. v is the pointer to the element returned by the previous next or start. Specific functions need to be implemented only when exit processing is required. But in many cases, it can be returned directly.

 

 
In the implementation of next and start, it may be necessary to traverse a sequence of functions, while in the kernel, the implementation of a sequence data structure generally uses a two-way linked list or hash linked list, all seq_file also provides some encapsulation interface functions for kernel bidirectional linked list and hash linked list, which is convenient for programmers to operate the structure sequence linked through the linked list. The names of these functions are generally seq_list_* Or seq_hlist_*, These functions are implemented in fs/seq_file.c, interested friends can have a look. In the later experiments, I still use the general two-way linked list API of the kernel.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programming steps:
 
Above, we introduced the use of seq_file needs to implement some functions and related structures. Now let's combine them and introduce the following using SEQ through proc_ File, while seq_ The application method of file in other aspects is the same.

(1) When registering the proc file entry, the registration includes seq_file of file operation function_ Operations structure to proc_fops. The example code in my test program is as follows:

proc_test_entry = create_proc_entry("proc_seq", 0644, NULL);
if (proc_test_entry == NULL) {
   ret = -ENOMEM;
   cleanup_test_data();
   pr_err("proc_test: Couldn't create proc entry\n");
} else {
   proc_test_entry->proc_fops = &proc_ops;
   pr_info("proc_test: Module loaded.\n");
}

(2) Implement struct file_operations proc_ops, for example:

static struct file_operations proc_ops = {
.owner = THIS_MODULE,
.open = proc_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};

As you can see, the above read, llseek and release all have SEQ_ The interface function provided by file can be registered directly. Only open needs to be implemented by itself.

(3) The implementation of this open function is also very simple and fixed format. Examples are as follows:

static int proc_seq_open(struct inode *inode, struct file *file)
{
     return seq_open(file, &proc_seq_ops);
};

You can see that SEQ is generally used_ An API in file: seq_open, the purpose is to send a message to SEQ_ Register a struct SEQ in the file structure_ operations .

 

(4) Implement {seq_operations, that is, SEQ we introduced earlier_ The underlying data operation function set of file, for example:

static struct seq_operations proc_seq_ops = {
.start = proc_seq_start,
.next = proc_seq_next,
.stop = proc_seq_stop,
.show = proc_seq_show
};

These callback functions need to be implemented according to the sequence data structure you want to obtain.

In fact, the actual programming process should be the opposite: (4) - > (3) - > (2) - > (1), but for better understanding, I will start with the final registration and then introduce the structure to be implemented.

OK, basic seq_ The use of file has been introduced, but we should really make good use of SEQ_ File is better to take a look at her implementation source code (fs/seq_file.c). It will be handy to use it only after understanding the internal principle.

For the test code used in the front and the test code of procfs in the front, it will be given in the next article, and the test records will be made.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Tags: Linux

Posted by v4g on Wed, 04 May 2022 05:51:16 +0300