【问题标题】:kernel module killed on using insmod内核模块在使用 insmod 时被杀死
【发布时间】:2017-06-17 19:09:20
【问题描述】:

我正在尝试将 cat 命令实现为内核模块。我知道文件 i/o 不应该在内核模块中完成。每次我使用 insmod module.ko 时,我都会得到输出为 killed 。我该如何解决?另外我该如何改进代码?
内核版本 - 4.4
代码:

#include <linux/module.h>   
#include <linux/kernel.h>
#include <linux/unistd.h>
#include <linux/syscalls.h> 
#include <linux/fcntl.h> 
#include <asm/uaccess.h>
#include <linux/init.h> 
#include <linux/fs.h>
#include <linux/file.h> 

static char* argv[10];
static int argc = 1;
module_param_array(argv, int, &argc , 0);

static void cat(int f, char *s)
{
    char buf[8192];
    struct file *file;
    loff_t pos = 0;
    long n;
    mm_segment_t old_fs = get_fs();
    set_fs(KERNEL_DS);

    while((n = vfs_read(f, buf, (long)sizeof buf, &pos)) > 0) 
    {
        //write(1, buf, n);
        file = fget(f);
        if (file) {
            vfs_write(file, buf, (long)sizeof buf, &pos);
            fput(file);
        }


    }
    set_fs(old_fs);
}

static void __init hello_init(void)
{
    int f, i;
    if(argc == 1)
        cat(0, "<stdin>");
    else for(i=1; i<argc; i++)
    {
        f = filp_open(argv[i], O_RDONLY, 0);
        if(f < 0)
            printk("error");
        else{
            cat(f, argv[i]);
            filp_close(f);
        }
    }
}

static void __exit hello_cleanup(void)
{
    printk(KERN_INFO "Cleaning up module.\n");
}

module_init(hello_init);
module_exit(hello_cleanup);

这是上述代码的简化版本,它只是读取一个文件。在注释掉 vfs_read 部分时,我可以对其进行安装,但使用 vfs_read 它显示已被杀死。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>

static void read_file(char *filename)
{
  int fd;
  char buf[1];
  loff_t f_pos = 0;

  mm_segment_t old_fs = get_fs();
  set_fs(KERNEL_DS);

  fd = filp_open(filename, O_RDONLY, 0);
  if (fd >= 0) {
    printk(KERN_DEBUG);
    while (vfs_read(fd, buf, 1, &f_pos) == 1) //if this
      printk("%c", buf[0]);  //and this is removed I'm able to insmod it
    printk("\n");
    sys_close(fd);
  }
  set_fs(old_fs);
}

static int __init init(void)
{
  read_file("/etc/shadow");
  return 0;
}

static void __exit exit(void)
{ }

module_init(init);
module_exit(exit);

【问题讨论】:

  • 您是否尝试过调试您的代码,以确定哪一行代码崩溃?对于调试内核代码,您可以在某些点之后添加printk() 调用。此外,如果代码崩溃,dmesg 可能会提供更多相关信息。
  • dmesg 给出 RIP [] vfs_read+0x5/0x130 和 fbcon_switch:检测到未处理的 fb_set_par 错误
  • 你的hello_init函数中的哪个分支被执行了? (是cat(0),还是cat(f))?请注意,您的模块接受 指针数组,这可能不是您所期望的。也就是说,argv[i] 是一个指向 .. 垃圾的指针,取消引用它(例如在 filp_open() 调用中)是一个明确错误。
  • 嗯,简化版很清楚。第一件事:filp_open() 返回struct file*(不是int),检查成功应该是!IS_ERR(f)。如果filp_open 成功,您可以将它返回的值传递给vfs_read()。使用fput(f) 执行关闭文件。
  • @Tsyvarev 非常感谢,它成功了

标签: c linux file io linux-kernel


【解决方案1】:

一些事情......

您正试图从模块初始化回调中完成所有这些操作。

argc/argv 里面什么都没有,所以这是个问题(即你可能会遇到段错误)。

您可以使用有效值硬连线argc/argv,但您必须重新编译并重新加载驱动程序并使用您想要分类的不同文件。

但最好接受模块参数并将其拆分为 strtok 等效于填充您的 argv。这是最接近驱动程序的 argv 等效项 [它的存在方式与应用程序中的 main 完全相同]。

您不必重新编译模块,但是,您仍然每次都必须重新加载它(每次给 insmod 一个不同的参数)

但是,真正/正确的方法是在模块 init 中使用标准机制将您的驱动程序注册为字符设备。同样,在模块清理例程中注销它。

然后,创建一个/dev 条目(例如/dev/mycat)。

从一个应用程序中,打开/dev/mycat,然后将您想要分类的文件列表写入文件描述符,每行一个。

拥有驱动程序的write 回调例程,解析您传递的缓冲区中的数据(即,就像您在用户空间中实现自己的fgets),并处理它获得的列表,执行cat 操作在每个文件上。

与创建/dev 条目相比,可能更容易创建/proc 条目(例如/proc/mycat)--YMMV

将文件列表传递给驱动程序的实际机制是任意的。您可以使用 AF_UNIX 套接字、命名管道、连接到 SysV 消息队列等,但 /dev 或 /proc 解决方案可能更容易。


更新:

vfs_read 中似乎还有另一个问题。 dmesg 给出 RIP [] vfs_read+0x5/0x130 和 fbcon_switch:检测到未处理的 fb_set_par 错误

除其他外,您将int 作为第一个参数传递给vfs_read。第一个参数必须是struct file * [就像vfs_write]。

注意:如果您使用标准机制构建驱动程序,这将使用-Wall 进行编译,并且会在编译时进行标记。

此外,即使您使用O_RDONLY(即相当于用户空间cat foobar &gt; foobar)打开它,您也正尝试将[cat 输出] 写入您正在读取的同一个文件。那是因为,对于vfs_write,你做了file = fget(f)。你真正想做的是file = fget(1)

对于vfs_read 和vfs_write,您需要fget/fput 配对。而且,它们需要分开:

file_in = fget(f);
file_out = fget(1);

// read/write loop ...
while (1) {
    ...
}

fput(file_in);
fput(file_out);

您确实不想将buf 放入堆栈。这会产生竞争条件。 [内核] 线程可能会迁移到另一个处理器,而 I/O 仍在 vfs_read [AFAIK] 上挂起。其他做类似事情的驱动程序使用kmalloc 来获取缓冲区[和kfree 来释放它]。

【讨论】:

  • 非常感谢。 vfs_read 中似乎还有另一个问题。 dmesg 给出 RIP [] vfs_read+0x5/0x130 和 fbcon_switch:检测到未处理的 fb_set_par 错误
猜你喜欢
  • 2014-10-20
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多