我已经使用eventfd()在用户空间程序中创建了一个eventfd实例。有没有一种方法可以将某些引用(指向其struct或pid +
fd对的指针)传递给已创建的eventfd实例到内核模块,以便它可以更新计数器值?
这是我想做的事情:我正在开发一个用户空间程序,该程序需要与我编写的内核空间模块交换数据和信号。为了传输数据,我已经在使用ioctl。但是我希望内核模块能够在新数据准备好通过ioctl进行使用时,向用户空间程序发出信号。
为此,我的用户空间程序将在各个线程中创建一些eventfds。这些线程将使用select()等待这些eventfds,并且每当内核模块更新这些eventfds的计数时,它们就会继续通过ioctl请求数据来消耗数据。
问题是,如何从内核空间解析指向这些eventfds的“结构文件*”指针?我可以向eventfds发送什么样的信息,以便它可以获取指向eventfds的指针?我将在内核模块中使用哪些函数来获取这些指针?
有没有更好的方法可以将事件从内核空间发送到用户空间?我不能放手使用select()。
我终于想出了怎么做。我意识到,系统上的每个打开文件都可以由打开该文件的进程之一的pid以及与该文件相对应的fd标识(在该进程的上下文中)。因此,如果我的内核模块知道pid和fd,则可以查找进程的
struct * task_struct ,并从中查找 struct *文件 ,最后使用fd,它可以获取指向eventfd的
struct *文件 的指针。然后,使用最后一个指针,它可以写入eventfd的计数器。
这是我为演示该概念而编写的用户空间程序和内核模块的代码(现已可用):
用户空间C代码(efd_us.c):
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> //Definition of uint64_t
#include <sys/eventfd.h>
int efd; //Eventfd file descriptor
uint64_t eftd_ctr;
int retval; //for select()
fd_set rfds; //for select()
int s;
int main() {
//Create eventfd
efd = eventfd(0,0);
if (efd == -1){
printf("\nUnable to create eventfd! Exiting...\n");
exit(EXIT_FAILURE);
}
printf("\nefd=%d pid=%d",efd,getpid());
//Watch efd
FD_ZERO(&rfds);
FD_SET(efd, &rfds);
printf("\nNow waiting on select()...");
fflush(stdout);
retval = select(efd+1, &rfds, NULL, NULL, NULL);
if (retval == -1){
printf("\nselect() error. Exiting...");
exit(EXIT_FAILURE);
} else if (retval > 0) {
printf("\nselect() says data is available now. Exiting...");
printf("\nreturned from select(), now executing read()...");
s = read(efd, &eftd_ctr, sizeof(uint64_t));
if (s != sizeof(uint64_t)){
printf("\neventfd read error. Exiting...");
} else {
printf("\nReturned from read(), value read = %lld",eftd_ctr);
}
} else if (retval == 0) {
printf("\nselect() says that no data was available");
}
printf("\nClosing eventfd. Exiting...");
close(efd);
printf("\n");
exit(EXIT_SUCCESS);
}
内核模块C代码(efd_lkm.c):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/fdtable.h>
#include <linux/rcupdate.h>
#include <linux/eventfd.h>
//Received from userspace. Process ID and eventfd's File descriptor are enough to uniquely identify an eventfd object.
int pid;
int efd;
//Resolved references...
struct task_struct * userspace_task = NULL; //...to userspace program's task struct
struct file * efd_file = NULL; //...to eventfd's file struct
struct eventfd_ctx * efd_ctx = NULL; //...and finally to eventfd context
//Increment Counter by 1
static uint64_t plus_one = 1;
int init_module(void) {
printk(KERN_ALERT "~~~Received from userspace: pid=%d efd=%d\n",pid,efd);
userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p\n",userspace_task);
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p\n",userspace_task->files);
rcu_read_lock();
efd_file = fcheck_files(userspace_task->files, efd);
rcu_read_unlock();
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's file struct: %p\n",efd_file);
efd_ctx = eventfd_ctx_fileget(efd_file);
if (!efd_ctx) {
printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.\n");
return -1;
}
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p\n",efd_ctx);
eventfd_signal(efd_ctx, plus_one);
printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1\n");
eventfd_ctx_put(efd_ctx);
return 0;
}
void cleanup_module(void) {
printk(KERN_ALERT "~~~Module Exiting...\n");
}
MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);
要运行此程序,请执行以下步骤:
内核模块 对于模块而言,引导选项只能用于直接编译到核心中的模块,格式是"模块名.选项=值",比如"usbcore.blinkenlights=1"。 动态加载的模块则可以在 modprobe 命令行上指定相应的选项值,比如"modprobe usbcore blinkenlights=1"。 可以使用"modinfo -p ${modulename}"命令显示可加载模块的所有可用选项。已经加载到内
主要内容:initramfe虚拟文件系统GRUB 加载了内核之后,内核首先会再进行二次系统的自检,而不一定使用 BIOS 检测的硬件信息。这时内核终于开始替代 BIOS 接管 Linux 的启动过程了。 内核完成再次系统自检之后,开始采用动态的方式加载每个硬件的模块,这个动态模块大家可以想象成硬件的驱动(默认 Linux 硬件的驱动是不需要手工安装的,如果是重要的功能,则会直接编译到内核当中;如果是非重要的功能,比如硬件驱动会编译为模块
Important 要用内核模块操作,必须有一个在运行的 Ceph 集群。 获取映像列表 要挂载块设备映像,先罗列出所有的映像。 rbd list 映射块设备 用 rbd 把映像名映射为内核模块。必须指定映像名、存储池名、和用户名。若 RBD 内核模块尚未加载, rbd 命令会自动加载。 sudo rbd map {pool-name}/{image-name} --id {user-name}
主要内容:内核模块保存位置与模块保存文件,内核模块的查看,内核模块的添加与删除Linux 的内核会在启动过程中自动检验和加载硬件与文件系统的驱动。一般这些驱动都是用模块的形式加载的,使用模块的形式保存驱动,可以不直接把驱动放入内核,有利于控制内核大小。 模块的全称是 动态可加载内核模块,它是具有独立功能的程序,可以被单独编译,但不能独立运行。模块是为内核或其他模块提供功能的代码集合。这些模块可以是 Linux 源码中自带的,也可以是由硬件厂商开发的(可以想象成驱动)。不过内
问题内容: 我尝试安装内核模块。它已成功安装在容器内。确实令人惊讶,但是没有在容器内或主机系统中列出此模块。如何在容器中装入新的内核模块?(容器,主机) 问题答案: 容器通过系统调用与内核交互,并且不包含内核的任何部分或容器内部的内核模块。这就是为什么容器设计轻巧且便于携带的原因之一。xfsprogs也是用户空间程序,而不是内核模块。 如何在容器中加载新的内核模块?(CentOS容器,Ubuntu
MANAGING THE LINUX KERNEL AND LOADABLE KERNEL MODULES 所有操作系统至少由两个主要组件组成。其中第一个也是最重要的是内核。 内核位于操作系统的中心,控制着操作系统所做的一切,包括管理内存,控制 CPU,甚至控制用户在屏幕上看到的内容。操作系统的第二个元素通常被称为用户区域,几乎包括其他所有元素。 内核被设计成一个受保护或特权的区域,只能由 roo