首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linux内核模块ioctl的两种不同功能原型

问Linux内核模块ioctl的两种不同功能原型
EN

Unix & Linux用户
提问于 2021-11-26 16:25:56
回答 2查看 686关注 0票数 0

正如在这个问题中指出的,Linux内核模块中的ioctl函数的原型是:

(第1版)

代码语言:javascript
复制
int ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg);

或

(第2版)

代码语言:javascript
复制
long ioctl(struct file *f, unsigned int cmd, unsigned long arg);

我想在实现字符设备驱动程序的内核模块中使用它们。

  1. 在这种情况下,上述两种原型是否都适合?如果是,为什么?如果没有,如何选择正确的?
  2. 哪些头/源文件(S)包含这些原型?换句话说:这些原型的官方参考文件是什么?

我正在x86_64上运行Ubuntu20.04,这些是我可用的头文件:

代码语言:javascript
复制
/usr/include/asm-generic/ioctl.h
/usr/include/linux/ioctl.h
/usr/include/linux/mmc/ioctl.h
/usr/include/linux/hdlc/ioctl.h
/usr/include/x86_64-linux-gnu/sys/ioctl.h
/usr/include/x86_64-linux-gnu/asm/ioctl.h

唯一重要的行是在/usr/include/x86_64-linux-gnu/sys/ioctl.h中:

代码语言:javascript
复制
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

但我在这里找不到关于上述两种替代原型的任何线索。

EN

回答 2

Unix & Linux用户

回答已采纳

发布于 2021-11-27 12:04:48

  1. 在这种情况下,上述两种原型是否都适合?如果是,为什么?如果没有,如何选择正确的?

两者都不合适。目前只有版本2在内核中可用,因此应该使用这个版本。

  1. 哪些头/源文件(S)包含这些原型?换句话说:这些原型的官方参考文件是什么?

它们位于include/linux/fs.h (这是相对于内核源代码根目录的路径),在struct file_operations定义中:

代码语言:javascript
复制
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

也就是说:成员unlocked_ioctl必须是指向函数的指针。

代码语言:javascript
复制
long ioctl(struct file *f, unsigned int cmd, unsigned long arg);

如果使用版本1在内核模块中定义了一个函数my_ioctl(),则会生成一个编译器错误:

代码语言:javascript
复制
error: initialization of ‘long int (*)(struct file *, unsigned int,  long unsigned int)’ from incompatible pointer type ‘long int (*)(struct inode *, struct file *, unsigned int,  long unsigned int)’ [-Werror=incompatible-pointer-types]
  .unlocked_ioctl = my_ioctl
                    ^~~~~~~~

Some附加注释

在内核2.6.10之前,版本1一直是唯一的版本,其中struct file_operations只有

代码语言:javascript
复制
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

然而,这个ioctl函数创建了一个大内核锁(BKL,BKL):它在运行期间锁定了整个内核。这是不可取的。所以,从2.6.11,

代码语言:javascript
复制
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

介绍了一种使用ioctls的新方法,它没有锁定内核。在这里,具有内核锁的旧ioctl和新的unlocked_ioctl共存。在2.6.36中,旧的ioctl已经被删除。所有驱动程序都应该相应地更新,以便只使用unlocked_ioctl。有关更多信息,请参考这个答案。

在最近的内核版本(5.15.2)中,使用旧ioctl的文件似乎仍然很少:

代码语言:javascript
复制
linux-5.15.2$ grep -r "ioctl(struct inode" *
Documentation/cdrom/cdrom-standard.rst: int cdrom_ioctl(struct inode *ip, struct file *fp,
drivers/staging/vme/devices/vme_user.c:static int vme_user_ioctl(struct inode *inode, struct file *file,
drivers/scsi/dpti.h:static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg);
drivers/scsi/dpt_i2o.c:static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
fs/fuse/ioctl.c:static int fuse_priv_ioctl(struct inode *inode, struct fuse_file *ff,
fs/btrfs/ioctl.c:static noinline int search_ioctl(struct inode *inode,
fs/ocfs2/refcounttree.h:int ocfs2_reflink_ioctl(struct inode *inode,
fs/ocfs2/refcounttree.c:int ocfs2_reflink_ioctl(struct inode *inode,
net/sunrpc/cache.c:static int cache_ioctl(struct inode *ino, struct file *filp,

然而,vme_user.c、dpt_i2o.c和cache.c有:

代码语言:javascript
复制
static const struct file_operations adpt_fops = {
        .unlocked_ioctl = adpt_unlocked_ioctl,

然后

代码语言:javascript
复制
static long adpt_unlocked_ioctl(struct file *file, uint cmd, ulong arg)
{
        struct inode *inode;
        long ret;

        inode = file_inode(file);

        mutex_lock(&adpt_mutex);
        ret = adpt_ioctl(inode, file, cmd, arg);

因此,他们在新的内部使用旧版本(根据Andy在评论中的建议,从可用数据中获取inode )。关于fs中的文件:它们似乎不使用struct file_operations;而且,它们的函数不是

代码语言:javascript
复制
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

因为它们采用不同的参数(fuse_priv_ioctl在fs/fuse/ioctl.c中,search_ioctl在fs/btrfs/ioctl.c中,ocfs2_reflink_ioctl在fs/ocfs2/refcounttree.c中),所以它们可能只在驱动程序内部使用。

因此,关联问题中有两个版本可用于Linux内核模块中的ioctl函数的假设是错误的。必须只使用unlocked_ioctl (版本2)。

票数 1
EN

Unix & Linux用户

发布于 2021-11-26 17:32:06

您正在查看在不同上下文中定义的函数。第三项:

代码语言:javascript
复制
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

是系统调用(即从用户空间到内核空间的调用)。

其他类似于内核中定义的函数( struct file和struct inode都是内核数据结构)。

某个用户空间程序对系统调用的调用将

代码语言:javascript
复制
   +-------------------+
   | userspace program |
   +-------------------+
            |
ioctl(fd, requestType, arg);
            |
            |                                           userspace
-------------------------------------------------------------------
            |                                           kernelspace
            v
SYSCALL_DEFINE3(ioctl...) /* ${kernel_root}/fs/ioctl.c */
            |
            v
      do_vfs_ioctl(...)
            |
 /*
   look at fd, map it to the device driver.  Call the ioctl
   registered for that device type.

   for example: drivers/char/random.c:

   const struct file_operations random_fops = {
        ...
        .unlocked_ioctl = random_ioctl,
        ...
   };
            |
            V
static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)

为什么有些是struct file而另一些是struct inode?我不确定,但我认为它可能取决于与给定文件描述符关联的设备类型(系统调用的fd参数)。VFS层可能会分派给不同类型的注册驱动程序。例如,设备驱动程序可能使用struct file,而文件系统驱动程序可能使用struct inode。

<#>编辑

如果您的问题是如何编写支持ioctl系统调用的字符设备驱动程序?那么下面是一个简单的示例:

代码语言:javascript
复制
#include 
#include 
#include 

static int example_device_major_number;
static const char example_device_name[] = "example-driver";

#define LOG(fmt, ...) printk(KERN_NOTICE "%s[%s:%d]: " fmt "\n", example_device_name, __FUNCTION__, __LINE__, ##__VA_ARGS__)

static long example_module_ioctl(struct file *, unsigned int cmd, unsigned long arg)
{
    LOG("cmd: %d, arg: %lu", cmd, arg);
    return 0;
}

static struct file_operations example_module_fops =
{
    .owner          = THIS_MODULE,
    .unlocked_ioctl = example_module_ioctl,
};

static int example_module_init(void)
{
    int result = 0;

    result = register_chrdev(0, example_device_name, &example_module_fops);
    if (result < 0)
    {
            LOG("Can't register character device with error code = %d", result);
            return result;
    }

    example_device_major_number = result;

    LOG("Registered character device with major number = %d", example_device_major_number);

    return 0;
}

static void example_module_exit(void)
{
    if (example_device_major_number != 0)
    {
        unregister_chrdev(example_device_major_number, example_device_name);
    }
    LOG("Module removed");
}

module_init(example_module_init);
module_exit(example_module_exit);
MODULE_LICENSE("GPL");

如果编译并加载该模块,我会在dmesg的输出中看到这一点:

代码语言:javascript
复制
[1325403.600381] example-driver[example_module_init:35]: Registered character device with major number = 238

从这一点上,我看到内核已经为我新增加的字符设备驱动程序分配了主要编号238。

现在,我可以用这个主要数字创建一个字符设备文件:

代码语言:javascript
复制
$ sudo mknod mydevice c 238 0
$ ls -l mydevice
crw-r--r-- 1 root root 238, 0 Nov 26 17:03 mydevice

接下来,我可以编写一个用户空间程序来(1)打开该设备文件,(2)对结果文件描述符调用ioctl():

代码语言:javascript
复制
#include 
#include 
#include 
#include 

int main(void)
{
    int fd = open("mydevice", O_RDWR);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    int rc = ioctl(fd, 1, 2);
    if (rc < 0) {
        perror("ioctl");
    }

    (void) close(fd);

    return 0;
}

在前面加载的模块之后,如果编译并运行用户空间应用程序,我将在dmesg的输出中看到以下内容

代码语言:javascript
复制
[1325593.158303] example-driver[example_module_ioctl:12]: cmd: 1, arg: 2
票数 2
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/679207

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档