【问题标题】:Why do some functions in the Linux kernel work differently for string variables and string literals?为什么 Linux 内核中的某些函数对字符串变量和字符串文字的工作方式不同?
【发布时间】:2017-10-08 20:06:37
【问题描述】:

在 Ubuntu 16.04 内核版本 4.4 中,可加载内核模块具有以下行为:

(使用字符串字面量)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h> 
#include <linux/mount.h>
#include <linux/path.h>
#include <linux/namei.h>
#include <linux/fs.h>

static int __init myinit(void)
{
    char *path_name = "~/microsoft.gpg";
    struct path path;

    printk("Module Init\n");

    if (kern_path(path_name, LOOKUP_FOLLOW, &path) < 0)
    {
        printk("kern_path fail\n");
        return 0;
    }
    printk("kern_path success\n");
    return 0;
}


static void __exit myexit(void)
{
    printk("Module Exit\n");
    return;
}

module_init(myinit); 
module_exit(myexit);

MODULE_LICENSE("GPL");

dmesg 的结果是

模块初始化

kern_path 成功

(使用字符串变量)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h> 
#include <linux/mount.h>
#include <linux/path.h>
#include <linux/namei.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/segment.h>

#define MAX_PATH_LEN 256

static char path_name[MAX_PATH_LEN];

static struct proc_dir_entry *proc_file;

static int myopen(struct inode *inode, struct file *file)
{
    printk("Module Open\n");
    return 0;
}

static ssize_t mywrite(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos)
{
    struct path path;
    ssize_t bytes = count < (MAX_PATH_LEN - (*ppos)) ? count : (MAX_PATH_LEN - (*ppos));

    if (copy_from_user(path_name, user_buffer, bytes))
        return -EFAULT;

    path_name[bytes] = '\0';

    printk("Module Write\n");

    if (kern_path(path_name, LOOKUP_FOLLOW, &path) < 0)
    {
        printk("kern_path fail\n");
        (*ppos) += bytes;
        return bytes;
    }
    printk("kern_path success\n");

    (*ppos) += bytes;
    return bytes;
}

static const struct file_operations fops = 
{
    .owner = THIS_MODULE,
    .open = myopen,
    .write = mywrite,
};

static int __init myinit(void)
{
    printk("Module Init\n");
    proc_file = proc_create("mymodule", 0644, NULL, &fops);
    return 0;
}


static void __exit myexit(void)
{
    printk("Module Exit\n");
    remove_proc_entry("mymodule", NULL);
    return;
}

module_init(myinit); 
module_exit(myexit);

MODULE_LICENSE("GPL");

在 shell 中输入的命令是

echo ~/microsoft.gpg > /proc/mymodule

dmesg 的结果是

模块初始化

模块打开

模块写入

kern_path 失败

我在写用户应用程序代码的时候没有遇到类似的问题,但是我真的很尴尬,因为内核模块有问题。

为什么问题会出现在第二个代码中?我该如何解决?


回答完成

【问题讨论】:

  • @UsagiMiyamoto 是一个指向“长偏移类型”对象的指针,表示用户正在访问的文件位置。
  • @UsagiMiyamoto 并且由于user_buffer 没有初始化,你需要在字符串的末尾添加'\0',使用count 表示输入的长度。
  • @BronislavElizavetin 不幸的是,即使我加上双引号甚至将“~”更改为“/root”,结果似乎也没有改变。第一个代码工作正常,即使我尝试其他文件,第二个代码似乎也无法正常工作。
  • @UsagiMiyamoto“文件位置”不影响路径和名称。但是,如果您在 file_operations 的读取操作中不增加“文件位置”,则模块将无限循环。
  • @UsagiMiyamoto 我只想使用 proc 模块获取目标文件的文件系统名称。无需修改内核源代码。

标签: c linux-kernel


【解决方案1】:

主要用于写入“内核文件”,echo 命令在字符串末尾附加换行符(这在documentation 中明确提到了echo)。

如果是换行符,内核模块可以通过丢弃最后一个输入符号来轻松处理这种情况:

if(path_name[bytes - 1] == '\n')
    path_name[bytes - 1] = '\0';

【讨论】:

  • 顺便提一下,紧随其后的用户(sysfs、configfs)和kstrto*()系列函数的kernfs是\n-aware的。
【解决方案2】:

在 cmets 中,@Tsyvarev 给了我正确的答案。我传递给 echo 的字符串包含换行符。我把 -n 选项去掉了。

谢谢,@Tsyvarev!

【讨论】:

    猜你喜欢
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-09
    相关资源
    最近更新 更多