Linux lee proc file seq

  

En linux a través del sistema de archivos proc para el estado del usuario y la interacción del estado del kernel o el ajuste fino del kernel es un medio relativamente simple y efectivo. Recientemente, debido a las necesidades del proyecto, debe enviar los datos de un módulo del kernel al modo de usuario a través de proc, y aprovechar la oportunidad de aprender seq_file. Haga un registro simple aquí, y el lugar equivocado es bienvenido para criticar y corregir. Para usar seq_file, necesita #include

Ok, no más tonterías, comencemos, ^ _ ^. Supongamos que, en uno de nuestros módulos del kernel, hay un conjunto de datos almacenados en estructuras de datos iterables (como listas vinculadas, arreglos, etc.), quiero enviarlos al estado del usuario a través de proc y verlos a través de cat, etc. Como hacerlo En aras de la simplicidad, supongamos que tenemos una lista de datos vinculados, el encabezado de la lista enlazada es apuntado por una variable llamada list_head, que a su vez está protegida con un bloqueo de lectura y escritura. La estructura de un nodo de lista enlazada es la siguiente:

struct node {

struct node * next;

int num;

};

Struct node * list_head; //encabezado de la tabla de la cadena

DEFINE_RWLOCK (list_lock); //Definir e inicializar un bloqueo de giro de lectura-escritura.

seq_file define un conjunto de punteros de función para la iteración, que funciona como un iterador hacia adelante en C ++, pero con un miembro llamado show para generar información del nodo. Este conjunto de punteros de función se declara en una estructura llamada struct seq_operations de la siguiente manera:

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); Br>

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

};

start puede hacer un trabajo de adquisición e inicialización de recursos, bastante c ++ constructor El rol, su valor de retorno se pasará a ellos como el segundo parámetro de la primera presentación de llamada y la siguiente, luego cada vez que llamará a la siguiente, y mostrará; comience si se devuelve NULL, entonces la parada de llamada directamente no llamará a la función de mostrar y Siguiente Si hay algunos encabezados para generar, puede regresar al kernel SEQ_START_TOKEN. Se define como:

#define SEQ_START_TOKEN ((void *) 1)

El valor de retorno de next se usará como el programa y la próxima llamada. El segundo argumento de next (es decir, v) se pasa a ellos, y cuando el siguiente devuelve NULL, se llama a stop.

La función de detención es similar a la función de los destructores en las clases de c ++, responsable de la limpieza de recursos y las operaciones de devolución.

La función show devuelve 0 correctamente, y el error devuelve el código de error correspondiente.

Tenga en cuenta que la información de salida es mayor que el búfer asignado por el kernel a la vez (como el motivo específico, la próxima vez que escriba sobre la implementación de seq_read y otras funciones, estará claro en ese momento) comience, muestre, a continuación , la parada se puede llamar varias veces, lo que requiere el tercer parámetro pos de start y next para distinguirlos. Por lo tanto, su orden de invocación se puede describir de la siguiente manera: inicio- > show- > next- > show- > next- > show- > ...- > next- > stop.

Tenga en cuenta que también puede ser el siguiente (iniciar la llamada de parada varias veces): start- > show- > next- > show- > next- > show- > ...- > next- > stop- > start- > show- > next ...- > next- > stop. Estas funciones iterativas se pueden definir en nuestro ejemplo de la siguiente manera:

static void * my_start (struct seq_file * m, l loff_t * pos)

{

int i = 1;

struct node * tmp_entry;

read_lock_bh (list_lock); //Obtiene un bloqueo de giro de lectura-escritura

if (* pos == 0) {

devolver SEQ_START_TOKEN;

}

para (tmp_entry = list_head; tmp_entry! = NULL; tmp_entry = tmp-entry- > next) {

if (i == * pos)

return tmp_entry;

++ i;

}

return NULL;

}

static void my_stop (struct seq_file * m, void * v)

{

read_unlock_bh (tmp_lock); //libere el bloqueo de giro

seq_printf (m, " \\ n \\ n ------------- fin de información de datos --------------------------- ");

}

void estático * my_next (struct seq_file * m, void * v, loff_t * pos)

{

++ * Pos;

return (SEQ_START_TOKEN == v)? List_head: ((struct node *) v) - &g t; next;

}

static int my_show (struct seq_file * m, void * v)

{

struct node * tmp_entry;

static int line_count = 0;

if (SEQ_START_TOKEN == v) {

seq_printf (m, " \\ n \\ n ---------- --- la información de los datos comienza --------------------------- \\ n \\ n ");

} else else < Br>

tmp_entry = (struct node *) v;

seq_file (m, "% d, quot; tmp_entry- > num);

if (++ line_count = = 10) {//Una línea genera diez datos

seq_printf (m, " \\ n ");

line_count = 0;

}

}

return 0;

}

Luego, defina una estructura seq_operations de la siguiente manera:

static const struct seq_operations my_seq_ops = {

.start = my_start,

.next = my_next,

.stop = my_stop,

.show = my_show,

};

En este punto, también debemos definir una función para enviar la dirección de my_seq_ops al kernel. De la siguiente manera,

static int proc_my_test_open ((struct inode * inode, struct file * file)

{

return seq_open (file, &my_seq_ops);

}

Finalmente, defina una estructura struct file_operations, nuestro proc_my_test_open se pasa al kernel, y se llama a proc_create, creando un directorio proc y listo. El código es el siguiente:

static const struct file_operations my_file_ops = {

.owner = THIS_MODULE,

.open = proc_my_test_open,

.read = seq_read, < Br>

.llseek = seq_lseek,

.release = seq_release,

};

my_file_opsproc_create (" my_test ", 0, NULL, &my_file_ops);

De hecho, para resultados simples, simplemente puedes definir una función de show y luego a través de single_open en lugar de nuestro seq_open aquí, este método es relativamente simple, el método específico puede ser google.

Copyright © Conocimiento de Windows All Rights Reserved