无法从模块访问内核函数符号 sys_epoll_create1

Cannot access kernel function symbol sys_epoll_create1 from module

我正在编写一个驱动程序作为模块。我必须从模块调用系统调用 sys_epoll_create1()。我写了一个这样的模块:

#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/net.h> 
#include <linux/syscalls.h> 
#include <linux/eventpoll.h> 
#include <net/sock.h> 
MODULE_LICENSE("GPL"); 
static int hello_init(void) 
{ 
    sys_epoll_create1(1); 
    return 0; 
} 

static void hello_exit(void) 
{ 

} 

module_init(hello_init); 
module_exit(hello_exit); 

编译日志是这样的:

~/test $ make 
make -C /lib/modules/4.2.0-16-generic/build M=/home/kyl/test modules 
make[1]: Entering directory '/usr/src/linux-headers-4.2.0-16-generic' 
CC [M]  /home/kyl/test/hello.o 
Building modules, stage 2. 
MODPOST 1 modules 
WARNING: "sys_epoll_create1" [/home/kyl/test/hello.ko] undefined! 
CC      /home/kyl/test/hello.mod.o 
LD [M]  /home/kyl/test/hello.ko 
make[1]: Leaving directory '/usr/src/linux-headers-4.2.0-16-generic' 

正如我检查的那样,linux/syscalls.h

中有一个sys_epoll_create1()的声明
asmlinkage long sys_epoll_create1(int flags);

我已经将 <linux/syscalls.h> 作为头文件,为什么 gcc 仍然显示 WARNING: "sys_epoll_create1" [/home/kyl/test/hello.ko] undefined!

Linux 内核不再导出 (EXPORT_SYMBOL) 系统调用实现(sys_* 函数)。例如,参见 this question 关于 sys_read 和 sys_open。与导出 vfs_* 替换的 reading/writing 文件不同,epoll 相关函数不会为模块导出,因此您不能 epoll_create1 文件描述符和 return 它给用户。

但是可以在内核模块中实现某种 select/poll 系统调用。为监视器存储文件描述符列表的方式可以是任意一种。

#include <linux/poll.h>

void my_select(void)
{
    struct poll_wqueues table;

    poll_initwait(&table);
    poll_table *wait = &table.pt;

    for(;;) {
        // Call ->poll() for every file descriptor(*i*) which is monitored
        for(<i in monitored files>) {
            struct fd f = fdget(i);
            if(f.file) {
                const struct file_operations *f_op = f.file->f_op;
                int mask_output = DEFAULT_POLLMASK; // Mask of available events
                int mask_input = <mask of events which are monitored for given file>;
                if(f_op) {
                    wait->_key = mask_input;
                    mask_output = f_op->poll(f.file, wait);
                }
                if(mask_output & mask_input) {
                    // Some requested events are available. Mark that fact in some way.
                }
            }
        }
        if(<some requested events have fired>) break;
        // Important: Make futher calls to ->proc not adding wait into waitqueue.
        wait->_proc = NULL;
        // Wait events if they are not fired already.
        // For timeout waits *poll_schedule_timeout* can be used.
        poll_schedule(&table, TASK_INTERRUPTIBLE);
    }
    poll_freewait(&table);
}

这实际上是核函数的简化实现do_select,所以你可以查看更多细节。

驱动程序中使用了类似的轮询机制serial2002。但它忘记在等待迭代之间清除 ->_proc 字段,因此在长时间等待和许多虚假唤醒的情况下可以获得 ENOMEM。