在32位模式和64位操作系统的64位模式下编译ioctl函数有什么不同?

我有一个64位的Enterprice SuSE 11我有一个应用程序打开一个HIDRAW设备,并运行一个ioctl函数来获取这个设备的原始信息,如下所示:

struct hidraw_devinfo devinfo; int fd = open("/dev/hidraw0", 0); int ret = ioctl(fd, HIDIOCGRAWINFO, &devinfo); ... 

如果我以64位模式编译这个程序,没有错误,也没有问题,当我执行应用程序时,ioctl函数可以正常工作。

 g++ main.cpp 

如果我用32位模式编译这个程序,也没有错误,也没有问题。 但是当我执行应用程序的ioctl函数返回EINVAL错误(errno = 22,无效的参数)

 g++ -m32 main.cpp 

有什么问题?

注意:

 struct hidraw_devinfo { __u32 bustype; __s16 vendor; __s16 product; } 

Linux ioctl定义和兼容性层是一个我刚才碰到的一个迷人的话题。

通常, ioctl定义使用一系列宏_IOW / _IOR等,它们将您的参数类型名称作为参考,以及一个魔术数字和序数值,用于给出您的ioctl参数值(例如HIDIOCGRAWINFO )。 类型名称用于将sizeof(arg_type)编码到定义中。 这意味着用户空间中使用的类型决定了由ioctl宏生成的 – 即HIDIOCGRAWINFO可能会因包含条件而异。

这里是32位和64位不同的第一点, sizeof可能会有所不同,取决于打包,使用模糊的数据大小(例如long),但特别是(和不可避免的),如果你使用指针参数。 所以在这种情况下,一个64位内核模块想要支持32位客户端,需要定义一个兼容性参数类型来匹配参数类型的32位等价格的布局,从而匹配一个32位兼容的ioctl。 这些32位等价的定义使用了一个称为compat的内核设施/层。

在你的情况下, sizeof()是相同的,所以这不是你正在采取的路径 – 但它是重要的了解可能发生的整个事情。

此外,内核配置可能会定义CONFIG_COMPAT ,这会改变sys-call包装器(特别是用户/内核接口和ioctl周围的代码),以减轻支持32位和64位的负担。 其中的一部分包括一个称为ioctl_compat的兼容性ioctl回调。

我所看到的是用CONFIG_COMPAT定义32位程序将生成将ioctl传递给ioctl_compat回调的代码,即使它可以生成与64位相同的ioctl值(例如在你的情况下)。 因此,驱动程序编写者需要确保ioctl_compat处理特殊(不同的)32位兼容的ioctl类型和普通的“64位或不变的32位”类型。

因此,在32位和64位系统上(没有CONFIG_COMPAT)设计和测试的内核模块可能适用于32位和64位程序,但不适用于支持两者的程序。

所以看在HID我看到这是2.6.38添加:

http://lxr.linux.no/#linux+v2.6.38/drivers/hid/hidraw.c#L347

问题可能是您的程序传递给ioctl函数的devinfo结构之间不匹配。

我猜你在64位系统上的工作。 因此,你的内核运行在64位,你正在与之交谈的内核模块(使用ioctl )也是64位。

以64位编译用户程序时,内核模块和用户程序中的devinfo定义是相同的。

以32位编译用户程序时,内核模块中的devinfo定义与用户程序中的定义不同。 实际上,在32位中,某些类型的大小发生了变化:主要是long和指针。 因此,你的程序创建一个特定大小的结构,而内核模块以不同的方式解释它接收到的数据。 内核模块可能不理解你给它的值,因为它没有在你放置它的位置上查找它。

解决方法是注意devinfo结构的定义,以便在编译32位和64位时具有相同的二进制表示。