内核模块因使用insmod而被杀死 [英] kernel module killed on using insmod

查看:102
本文介绍了内核模块因使用insmod而被杀死的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将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>静态字符* argv [10];static int argc = 1;module_param_array(argv,int,& argc,0);静态void cat(int f,char * s){char buf [8192];结构文件* file;loff_t pos = 0;长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);文件= fget(f);如果(文件){vfs_write(file,buf,(long)sizeof buf,& pos);fput(文件);}}set_fs(old_fs);}静态void __init hello_init(void){int f,我;if(argc == 1)cat(0,< stdin>");否则(i = 1; i< argc; i ++){f = filp_open(argv [i],O_RDONLY,0);如果(f <0)printk(错误");别的{cat(f,argv [i]);filp_close(f);}}}静态void __退出hello_cleanup(void){printk(KERN_INFO正在清理模块.\ 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>静态无效read_file(char *文件名){int fd;char buf [1];loff_t f_pos = 0;mm_segment_t old_fs = get_fs();set_fs(KERNEL_DS);fd = filp_open(文件名,O_RDONLY,0);如果(fd> = 0){printk(KERN_DEBUG);while(vfs_read(fd,buf,1,& f_pos)== 1)//如果是printk(%c",buf [0]);//这已删除,我可以对其进行设置printk("\ n");sys_close(fd);}set_fs(old_fs);}静态int __init init(void){read_file("/etc/shadow");返回0;}静态void __exit exit(void){}module_init(init);module_exit(退出); 

解决方案

几件事...

您正在尝试通过模块初始化回调执行所有这些操作.

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 作为第一个arg传递给 vfs_read .第一个arg必须是 struct file * [就像 vfs_write ].

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

注意:>

此外,即使您使用 O_RDONLY (即相当于用户空间 cat foobar)打开了文件,您仍试图将[cat输出]写入要读取的文件中> foobar ).这是因为,对于 vfs_write ,您做了 file = fget(f).您真正想做的是 file = fget(1)

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

  file_in = fget(f);file_out = fget(1);//读/写循环...而(1){...}fput(file_in);fput(file_out); 

不是要把 buf 放在堆栈上.这产生了竞争条件.当[code] vfs_read [AFAIK]上的I/O仍处于挂起状态时,[kernel]线程可以迁移到另一个处理器.其他执行类似操作的驱动程序使用 kmalloc 获取缓冲区[和 kfree 释放缓冲区].

I am trying to implement cat command as a kernel module. I know file i/o should not be done in a kernel module,. Every time I use insmod module.ko I get the output as killed . How can I fix it? Also how can I improve the code?
kernel version - 4.4
Code:

#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);

Here is a simplified version of the above code which just reads a file. On commenting out the vfs_read part, I'm able to insmod it, but with vfs_read it shows killed.

#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);

解决方案

A few things ...

You're trying to do all this from the module init callback.

argc/argv have nothing in them, so that's a problem (i.e. you could get a segfault).

You could hardwire argc/argv with valid values, but you'd have to recompile and reload the driver with the different files you want to cat.

But it might be better to accept a module parameter and split it up with a strtok equivalent to populate your argv. This is the closest thing to an argv equivalent for a driver [which doesn't really exist in exactly the same way as it does for main in an application].

You wouldn't have to recompile the module, but, you would still have to reload it each time (giving insmod a different argument each time)

However, the real/correct way to do this would be, in the module init, to register your driver as a character device using the standard mechanism. And, likewise, unregister it in the module cleanup routine.

Then, create a /dev entry (e.g. /dev/mycat).

From an application, open /dev/mycat, and write a list of files you want cat'ed to the file descriptor, one per line.

Have the driver's write callback routine, parse down the data in the buffer you're passed (i.e. as if you were implementing your own fgets in userspace), and process the list it gets, performing the cat operation on each file.

Rather than creating a /dev entry, it might be easier to create a /proc entry (e.g. /proc/mycat)--YMMV

The actual mechanism to communicate the file list to the driver is arbitrary. You could use an AF_UNIX socket, a named pipe, hook up to a SysV message queue, etc. but the /dev or /proc solution is probably easier.


UPDATE:

There seems to be another problem in vfs_read. dmesg gives RIP [] vfs_read+0x5/0x130 and fbcon_switch: detected unhandled fb_set_par error

Amongst other things, you are passing an int as the first arg to vfs_read. The first arg needs to be struct file * [just like vfs_write].

Note: If you're building the driver using the standard mechanism, this would have compiled with -Wall and this would have been flagged at compile time.

Also, you're trying to write [the cat output] to the same file you're reading from, even though you opened it with O_RDONLY (i.e. equivalent to userspace cat foobar > foobar). That's because, for the vfs_write, you did file = fget(f). What you really wanted to do was file = fget(1)

For both vfs_read and vfs_write, you need fget/fput pairings. And, they need to be separate:

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

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

fput(file_in);
fput(file_out);

You do not want to put buf on the stack. This produces a race condition. The [kernel] thread could migrate to another processor while I/O is still pending on the vfs_read [AFAIK]. Other drivers that do similar stuff use kmalloc to get the buffer [and kfree to free it].

这篇关于内核模块因使用insmod而被杀死的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆