我正在尝试将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
部分时,我可以对其进行insmod,但是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);
答案 0 :(得分:1)
一些事情......
您正在尝试从模块init回调中完成所有这些操作。
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
打开它(即相当于用户空间{{1} })。这是因为,对于cat foobar > foobar
,您执行了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);
放在堆栈上。这会产生竞争条件。 [Iker]线程可以迁移到另一个处理器,而I / O仍在buf
[AFAIK]上待处理。执行类似操作的其他驱动程序使用vfs_read
来获取缓冲区[和kmalloc
以释放缓冲区。)