【问题标题】:Cannot access kernel function symbol sys_epoll_create1 from module无法从模块访问内核函数符号 sys_epoll_create1
【发布时间】:2016-03-23 13:45:47
【问题描述】:

我正在编写一个驱动程序作为模块。我必须从模块调用系统调用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);

我已经包含&lt;linux/syscalls.h&gt;作为头文件,为什么gcc仍然显示WARNING: "sys_epoll_create1" [/home/kyl/test/hello.ko] undefined!

【问题讨论】:

  • 您是否尝试修改内核代码以将此类系统调用导出到内核的其余部分?
  • @Claudio 那是我最后的选择。我更喜欢在不修改内核的情况下构建模块。
  • Linux 内核不再导出(EXPORT_SYMBOL)系统调用实现(sys_* 函数)。参见,例如,this question 关于sys_readsys_open。与具有导出 vfs_* 替换的读/写文件不同,epoll 相关函数不会为模块导出,因此您不能epoll_create1 文件描述符并将其返回到用户。但是,如果您只想轮询内核中的一些文件集,有办法做到这一点。
  • @Tsyvarev 是否​​可以将fs/eventpoll.c 复制到我的模块源代码树中并使用重复的eventpoll.c 构建?
  • 你可以试试,但 eventpoll 文件描述符中的循环检测将不起作用。实际上,在内核中使用 epoll 看起来很奇怪。但是,如果不了解您的最终目的,就很难提出建议。

标签: kernel linux-device-driver system-calls epoll


【解决方案1】:

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

但是可以在内核模块中实现某种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 中使用了类似的轮询机制。但它忘记在等待迭代之间清除 -&gt;_proc 字段,因此在长时间等待和许多虚假唤醒的情况下可以获得 ENOMEM。

【讨论】:

  • 非常感谢。这些信息对我很有帮助!
猜你喜欢
  • 2017-03-18
  • 1970-01-01
  • 2018-05-20
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
相关资源
最近更新 更多