2015-05-04 9 views
10

come so, per informare lo spazio utente dallo spazio del kernel, un modo è quello di utilizzare il sondaggio. Ciò significa che il driver del kernel dovrebbe fornire prima il metodo di polling. Il codice di sotto è stato trovato da internet, e funziona davvero!Come aggiungere la funzione di polling al codice del modulo del kernel?

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

che posso fare in questo modo per farlo funzionare:

echo "hello" > /proc/fortune 

E poi

cat /proc/fortune 

per vedere il risultato.

Ma come aggiungere un metodo di polling ad esso? Ho provato alcune volte, ma ancora fallito. Qualcuno potrebbe aiutare? Grazie!

risposta

9

È possibile trovare alcuni buoni esempi nel kernel stesso. Date un'occhiata al prossimo file:

Per aggiungere poll() funzione per il codice di seguire i seguenti passi.

  1. intestazioni Includi necessari:

    #include <linux/wait.h> 
    #include <linux/poll.h> 
    
  2. Declare waitqueue variabili:

    static DECLARE_WAIT_QUEUE_HEAD(fortune_wait); 
    
  3. Aggiungere fortune_poll() funzione e aggiungerlo (come .poll callback) per la vostra struttura di operazioni sui file:

    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, 
    }; 
    

    Si noti che è necessario restituire POLLIN | POLLRDNORM se si dispone di alcuni nuovi dati da leggere e 0 nel caso in cui non ci siano nuovi dati da leggere (poll() chiamata scaduta). Vedi man 2 poll per i dettagli.

  4. Informare il vostro waitqueue una volta che hai i nuovi dati:

    wake_up_interruptible(&fortune_wait); 
    

Ecco le cose di base circa l'attuazione di poll() operazione. A seconda dell'attività, potrebbe essere necessario utilizzare nella funzione .read (come wait_event_interruptible()).


Vedere anche la domanda correlata: Implementing poll in a Linux kernel module.

+0

La domanda è stata molto utile per me, ma io sono ancora confuso. Ho una domanda correlata qui: https://stackoverflow.com/questions/34027366/implementing-poll-in-a-linux-kernel-module – zmb

3

Minimal esempio eseguibile

utilizzati:

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

Esito: ogni secondo, la seguente è stampato a schermo:

loop 
POLLIN n=10 buf=<jiffies> 

GitHub monte con QEMU + Buildroot boilerplate: poll.ko, poll.out

In questo esempio semplificato, generiamo eventi di polling da un thread separato. Nella realtà, gli eventi di polling saranno probabilmente innescati da interruzioni, quando l'hardware ha terminato un lavoro e sono stati resi disponibili nuovi dati per l'utente da leggere.

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://stackoverflow.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); 
     } 
    } 
} 
Problemi correlati