Как добавить функцию опроса в код модуля ядра?
как я знаю, чтобы информировать пространство пользователя из пространства ядра, одним из способов является использование опроса. Это означает, что драйвер ядра должен сначала предоставить метод опроса.
Ниже код найден из Интернета, и он действительно работает!
#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 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 );
Я могу сделать это, чтобы он работал:
echo "hello" > /proc/fortune
И затем
cat /proc/fortune
чтобы увидеть результат.
Но как добавить к нему метод опроса? Я пробовал несколько раз, но все равно не смог.
Может ли кто-нибудь помочь? Спасибо!
Ответы
Ответ 1
Вы можете найти несколько хороших примеров в самом ядре. Взгляните на следующие файлы:
Чтобы добавить функцию poll()
к вашему коду, выполните следующие действия.
-
Включить необходимые заголовки:
#include <linux/wait.h>
#include <linux/poll.h>
-
Объявить переменную waitqueue:
static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
-
Добавьте функцию fortune_poll()
и добавьте ее (как .poll
обратный вызов) в вашу файловую структуру:
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,
};
Обратите внимание, что вы должны вернуть POLLIN | POLLRDNORM
, если у вас есть новые данные для чтения, и 0
, если нет новых данных для чтения (poll()
время ожидания вызова). Подробнее см. man 2 poll.
-
Сообщать о своем ожидании после получения новых данных:
wake_up_interruptible(&fortune_wait);
Это основные сведения об осуществлении операции poll()
. В зависимости от вашей задачи вам может понадобиться использовать waitqueue API в вашей функции .read
(например, wait_event_interruptible()
).
См. также соответствующий вопрос: Реализация опроса в модуле ядра Linux.
Ответ 2
Минимальный пример runnable
Использование:
insmod /poll.ko
mount -t debugfs none /sys/kernel/debug
/poll.out /sys/kernel/debug/lkmc_poll/f
Результат: каждую секунду на экран выводится следующее:
loop
POLLIN n=10 buf=<jiffies>
GitHub вверх по течению с QEMU + шаблоном Buildroot: poll.ko, poll.out
В этом упрощенном примере мы генерируем события опроса из отдельного потока. В реальной жизни события опроса, скорее всего, будут вызваны прерываниями, когда аппаратное обеспечение завершит какую-то работу, и новые данные станут доступными для чтения пользователем.
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/info/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);
}
}
}