write the simplest programming program make how guide linux linux-kernel driver

linux - programming - the simplest kernel module



¿Cómo agregar la función de sondeo al código del módulo del núcleo? (2)

Como sé, para informar al espacio del usuario desde el espacio del kernel, una forma es usar sondeo. Eso significa que el controlador del núcleo debe proporcionar primero el método de sondeo. El siguiente código se encuentra en Internet, ¡y realmente funciona!

#include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/string.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Fortune Cookie Kernel Module"); MODULE_AUTHOR("M. Tim Jones"); #define MAX_COOKIE_LENGTH PAGE_SIZE static struct proc_dir_entry *proc_entry; static char *cookie_buf; // Space for fortune strings static int write_index; // Index to write next fortune static int read_index; // Index to read next fortune ssize_t fortune_write( struct file *filp, const char __user *buff, unsigned long len, void *data ) // Refer to: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); { int space_available = (MAX_COOKIE_LENGTH-write_index); if (len > space_available) { printk(KERN_INFO "fortune: cookie buffer is full!/n"); return -ENOSPC; } if (copy_from_user( &cookie_buf[write_index], buff, len )) { return -EFAULT; } write_index += len; cookie_buf[write_index-1] = 0; return len; } ssize_t fortune_read(struct file *file, char *buf, size_t count, loff_t *f_pos){ // Refer to: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); int len; //there''s no fortune or a fortune has already been read //the *f_pos > 0 hack is needed because `cat /proc/fortune` would otherwise //display every thing in the cookie_buf if(write_index == 0 || *f_pos > 0){ return 0; } // cicle through fortunes if(read_index >= write_index){ read_index = 0; } len = sprintf(buf, "%s/n", &cookie_buf[read_index]); read_index += len; *f_pos += len; return len; } static const struct file_operations proc_test_fops = { .owner = THIS_MODULE, // .open = led_proc_open, .read = fortune_read, // .llseek = seq_lseek, // .release = single_release, .write = fortune_write, // unsigned int (*poll) (struct file *, struct poll_table_struct *); // int (*fasync) (int, struct file *, int); }; int __init init_fortune_module( void ) { int ret = 0; cookie_buf = (char *)vmalloc( MAX_COOKIE_LENGTH ); if (!cookie_buf) { ret = -ENOMEM; } else { memset( cookie_buf, 0, MAX_COOKIE_LENGTH ); // proc_entry = create_proc_entry( "fortune", 0644, NULL ); proc_entry = proc_create( "fortune", 0644, NULL, &proc_test_fops ); if (proc_entry == NULL) { ret = -ENOMEM; vfree(cookie_buf); printk(KERN_INFO "fortune: Couldn''t create proc entry/n"); } else { write_index = 0; read_index = 0; printk(KERN_INFO "fortune: Module loaded./n"); } } return ret; } void __exit exit_fortune_module( void ) { // remove_proc_entry("fortune", &proc_entry); proc_remove(proc_entry); vfree(cookie_buf); printk(KERN_INFO "fortune: Module unloaded./n"); } module_init( init_fortune_module ); module_exit( exit_fortune_module );

Puedo hacer esto para que funcione:

echo "hello" > /proc/fortune

Y entonces

cat /proc/fortune

para ver el resultado

¿Pero cómo agregarle un método de encuesta? Lo intenté algunas veces, pero aún fallé. ¿Alguien podría ayudar? ¡Gracias!


Puede encontrar algunos buenos ejemplos en el núcleo mismo. Echa un vistazo a los siguientes archivos:

Para agregar la función poll() a su código, siga los siguientes pasos.

  1. Incluir encabezados necesarios:

    #include <linux/wait.h> #include <linux/poll.h>

  2. Declara la variable waitqueue:

    static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);

  3. Agregue la función fortune_poll() y agréguela (como .poll llamada .poll ) a la estructura de operaciones de su archivo:

    static unsigned int fortune_poll(struct file *file, poll_table *wait) { poll_wait(file, &fortune_wait, wait); if (new-data-is-ready) return POLLIN | POLLRDNORM; return 0; } static const struct file_operations proc_test_fops = { .... .poll = fortune_poll, };

    Tenga en cuenta que debe devolver POLLIN | POLLRDNORM POLLIN | POLLRDNORM si tiene algunos datos nuevos para leer, y 0 en caso de que no haya datos nuevos para leer (llamada de poll() expiró). Vea la encuesta man 2 para más detalles.

  4. Notifique su cola de espera una vez que tenga nuevos datos:

    wake_up_interruptible(&fortune_wait);

Eso es lo básico sobre la implementación de la operación poll() . Dependiendo de su tarea, es posible que necesite usar alguna API de waitqueue en su función wait_event_interruptible() como wait_event_interruptible() ).

Consulte también la pregunta relacionada: Implementar la encuesta en un módulo de kernel de Linux .


Ejemplo ejecutable mínimo

Uso:

insmod /poll.ko mount -t debugfs none /sys/kernel/debug /poll.out /sys/kernel/debug/lkmc_poll/f

Resultado: cada segundo, se imprime lo siguiente en la pantalla:

loop POLLIN n=10 buf=<jiffies>

GitHub upstream con QEMU + Buildroot repetitivo: poll.ko , poll.out

En este ejemplo simplificado, generamos eventos de sondeo desde un hilo separado. En la vida real, los eventos de la encuesta probablemente se desencadenarán por interrupciones, cuando el hardware haya terminado algún trabajo y haya nuevos datos disponibles para que los usuarios los lean.

poll.ko:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */ #include <linux/debugfs.h> #include <linux/delay.h> /* usleep_range */ #include <linux/errno.h> /* EFAULT */ #include <linux/fs.h> #include <linux/jiffies.h> #include <linux/kernel.h> /* min */ #include <linux/kthread.h> #include <linux/module.h> #include <linux/poll.h> #include <linux/printk.h> /* printk */ #include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible */ #include <uapi/linux/stat.h> /* S_IRUSR */ MODULE_LICENSE("GPL"); static char readbuf[1024]; static size_t readbuflen; static struct dentry *dir; static struct task_struct *kthread; static wait_queue_head_t waitqueue; static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off) { ssize_t ret; if (copy_to_user(buf, readbuf, readbuflen)) { ret = -EFAULT; } else { ret = readbuflen; } /* This is normal pipe behaviour: data gets drained once a reader reads from it. */ /* https://.com/questions/1634580/named-pipes-fifos-on-unix-with-multiple-readers */ readbuflen = 0; return ret; } /* If you return 0 here, then the kernel will sleep until an event happens in the queue. This gets called again every time an event happens in the wait queue. */ unsigned int poll(struct file *filp, struct poll_table_struct *wait) { poll_wait(filp, &waitqueue, wait); if (readbuflen) return POLLIN; else return 0; } static int kthread_func(void *data) { while (!kthread_should_stop()) { readbuflen = snprintf(readbuf, sizeof(readbuf), "%llu", (unsigned long long)jiffies); usleep_range(1000000, 1000001); wake_up(&waitqueue); } return 0; } static const struct file_operations fops = { .owner = THIS_MODULE, .read = read, .poll = poll }; static int myinit(void) { dir = debugfs_create_dir("lkmc_poll", 0); debugfs_create_file("f", S_IRUSR | S_IWUSR, dir, NULL, &fops); init_waitqueue_head(&waitqueue); kthread = kthread_create(kthread_func, NULL, "mykthread"); wake_up_process(kthread); return 0; } static void myexit(void) { kthread_stop(kthread); debugfs_remove_recursive(dir); } module_init(myinit) module_exit(myexit)

poll.out userland:

#define _XOPEN_SOURCE 700 #include <fcntl.h> /* creat, O_CREAT */ #include <poll.h> /* poll */ #include <stdio.h> /* printf, puts, snprintf */ #include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */ #include <unistd.h> /* read */ int main(int argc, char **argv) { char buf[1024], path[1024]; int fd, i, n; short revents; struct pollfd pfd; fd = open(argv[1], O_RDONLY | O_NONBLOCK); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } pfd.fd = fd; pfd.events = POLLIN; while (1) { puts("loop"); i = poll(&pfd, 1, -1); if (i == -1) { perror("poll"); exit(EXIT_FAILURE); } revents = pfd.revents; if (revents & POLLIN) { n = read(pfd.fd, buf, sizeof(buf)); printf("POLLIN n=%d buf=%.*s/n", n, n, buf); } } }