Because PROCFS's default action function uses only one page of caching, it is troublesome to handle large proc files, and is less flexible when outputting data from a series of structures, and needs to implement iterations in the Read_proc function, which is prone to bugs. So the kernel hackers have done some research on the/proc code, abstracting the commonality, and eventually formed the Seq_file (Sequence file: Sequence files) interface. This interface provides a simple set of functions to solve the problems of the above proc interface programming, making programming easier and reducing the chance of bugs appearing.
The Seq_file interface is recommended when you need to create a virtual file or a larger virtual file that is composed of a series of data sequences. But I personally think, not only PROCFS can use this seq_file interface, because in fact, Seq_file is implemented is a set of operating functions, this function set is not bound to proc, the same can be used in other places. For a function interface layer learning, first of all to look at a related data structure struct Seq_file:
Cached page pointers used by the
include/linux/seq_file.h
struct seq_file {
char *buf; //seq_file interface
size_t size; //seq_file interface uses cached page size size_t from; //offset from Seq_file when copying to a user-state buffer
The number of characters in the size_t count; //buf that can be copied to the user state
loff_t index; //start, Next's processed subscript pos value
loff_t read_pos; //Current amount of data copied to user state
U64 version;
struct Mutex lock; //Mutex for this seq_file operation, all seq_* access will be locked
const struct Seq_operations *op; Functions that manipulate the actual underlying data
Void *private
}; In this structure, almost all members are handled by Seq_file internal implementations, and programmers don't have to care unless you're going to study seq_ The internal principle of file. For this structure, the only thing the programmer has to do is to implement the Const struct Seq_operations *op. To access different data structures using the Seq_file interface, you have to create a simple set of object iterative operations 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);
};
The Seq_file internal mechanism uses these interface functions to access the actual data structure body at the bottom, and continues to move forward along the sequence, while outputting the data from the sequence to the Seq_file cache (size one page). In other words, seq_file internal mechanism to help you achieve the sequence of data reading and caching mechanism, you only need to implement the underlying iterative function interface is good, because these are related to the underlying data you want to access, and seq_file belong to the upper abstraction. This may seem a bit complicated, as you can see in the following picture:
Here we outline the functions of the following struct seq_operations:
void * (*start) (struct seq_file *m, loff_t *pos); The Start method is invoked first, and its purpose is to set the starting point of the access. M: refers to the structure of the seq_file, which is normally not handled. POS: is an integer position value that indicates where to begin reading. The meaning of this position depends entirely on the underlying implementation, which is not necessarily the location of the byte unit, but may be the sequence number of an element. The return value, if not NULL, is a private data structure body pointer to the iterator implementation. Returns NULL if the access error occurs.
Set the access starting point, the seq_file internal mechanism might use the Show method to get data from the structure that the start return value points to to the internal cache and send it to the user space at the right time.
Int (*show) (struct seq_file *m, void *v);
So the Show method is responsible for exporting V to the Seq_file internal cache of data in the element, but it must be provided with the help of some of the seq_file-like interface functions:
int seq_printf (struct seq_file *sfile, const char *fmt, ...);
A printf-like function designed for Seq_file, and a format string and value-added parameter commonly used for data.
You must pass the SET_FILE structure pointer to the show function to it. If seq_printf returns-1, it means that the buffer is full and some of the output is discarded. But most of the time the return value is ignored.
int SEQ_PUTC (struct seq_file *sfile, char c);
int seq_puts (struct seq_file *sfile, const char *s);
Functions similar to the PUTC and puts functions, sfile parameters and return values are the same as seq_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 the characters in s that appear in ESC to the cache in octal format. The common value of ESC is "\t\n\\", which keeps the embedded spaces from messing up the output or confusing the shell script.
int seq_write (struct seq_file *seq, const void *data, size_t len)//directly writes data pointed to the Seq_file cache with data length len. Used for non-string data.
int Seq_path (struct seq_file *sfile, struct vfsmount *m, struct dentry, char *dentry);
This function can be used to output the file name associated with a given directory item, and is rarely used by the driver.
After the show function returns, the seq_file mechanism may need to move to the next data element, and you must use the next method.
void * (*next) (struct seq_file *m, void *v, loff_t *pos); V: is the element pointer that was returned before start or next, possibly the element to which the previous show has finished outputting. POS: The element index value that needs to be moved to.
The value that the POS points to should be incremented in the next implementation, but the number of increments is related to the implementation of the iterator, not necessarily 1. The return value of next, if not NULL, is the next element pointer that needs to be output to the cache, otherwise the Stop method will be called for cleanup if the output ends.
void (*stop) (struct seq_file *m, void *v);
In the stop implementation, the parameter m points to the structure of the seq_file and is not normally handled. and V refers to the element pointer that is returned by a next or start up. When you need to do exit processing, you need to implement specific features. However, in many cases, you can return directly.
In the implementation of next and start may need to traverse a sequence of functions, while in the kernel, for a sequence of data structure of the implementation of the general use of a two-way linked list or hash list, all seq_file at the same time provide some for the core two-way linked lists and hash list of the package interface functions, Facilitates the programmer to implement the operation of the sequence of structures through the chain link. These function names are generally seq_list_* or seq_hlist_*, and these functions are implemented in fs/seq_file.c, and interested friends can look at them. I still use the Universal two-way list API of the kernel in later experiments. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Programming Steps:
In the above we introduce some of the functions and related structures that need to be implemented using Seq_file, and now we combine them to introduce the following general steps to use Seq_file through proc, and seq_file the same way in other ways.
(1) When registering the proc file entry, register the file_operations structure containing the seq_file operation function to Proc_fops. The instance 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, as follows: 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 are all Seq_file