【问题标题】:How can I do 64 bit division on linux kernel? [duplicate]如何在 linux 内核上进行 64 位除法? [复制]
【发布时间】:2018-08-06 13:00:02
【问题描述】:

我想在 linux 内核(32 位处理器)上执行以下代码:

#define UQ64 long long int
#define UI32 long int

UQ64 qTimeStamp;
UQ64 qSeconds;
UI32 uTimeStampRes;

qTimeStamp = num1;
uTimeStampRes = num2;

// 64 division !
qSeconds = qTimeStamp / uTimeStampRes;

有计算64除法的算法吗? 谢谢。

【问题讨论】:

  • 您发布的代码不起作用?
  • 可以使用支持这种操作的机器上的编译器来划分任何位数。 32 位机器上的 gcc 符合 C 标准,这意味着它支持 64 位操作(使用 至少 64 位长的long long int 类型)。您可以在 8 位架构上划分 64 位数字,前提是您的编译器可以生成正确的代码。附带说明一下,gcc 作为扩展支持__int128 类型(和操作)
  • 哎呀,我开始怀疑自己了,还是内核空间不同,我是wrong
  • @KamilCuk 它只是尝试微优化代码而不使用库函数。没有别的了。
  • 请注意,long int 在 64 位内核上将是 64 位。如果您真的想要内核中的 32 位整数,普通的 int 会更好。此外,对于 signed 类型,以 U 开头的类型名的命名约定令人困惑!

标签: c linux-kernel


【解决方案1】:

GCC C 编译器生成调用 libgcc 库中的函数的代码,以在 32 位 CPU 上使用 64 位操作数实现 /% 操作。但是,Linux 内核没有链接到 libgcc 库,因此在为 32 位 Linux 内核构建代码时,此类代码将无法链接。 (在构建外部内核模块时,在您尝试将模块动态加载到正在运行的内核中之前,问题可能并不明显。)

最初,Linux 内核只有#include <asm/div64.h> 定义的do_div(n,base) 宏。这个宏的用法很不寻常,因为它就地修改了它的第一个参数,使其成为除法的商,并产生(返回)除法的余数作为结果。这样做是出于代码效率的原因,但使用起来有点麻烦。此外,它仅支持 64 位无符号除数除以 32 位除数。

Linux 内核版本 2.6.22 引入了 #include <linux/math64.h> 标头,它定义了一组函数,比旧的 do_div(n,base) 宏更全面,更易于使用,因为它们的行为类似于普通的 C 函数。

#include <linux/math64.h> 为 64 位除法声明的函数如下所示。除非另有说明,所有这些都自内核版本 2.6.26 起可用。

italics 中列出的函数之一在内核版本 4.18-rc8 中尚不存在。谁知道它是否会实施? (后面的内核版本中头文件声明的其他一些与乘法和移位操作相关的函数,下面省略了。)

  • u64 div_u64(u64 dividend, u32 divisor) — 64 位除数除以 32 位除数的无符号除法。
  • s64 div_s64(s64 dividend, s32 divisor) — 64 位除数除以 32 位除数的有符号除法。
  • u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) — 64 位被除数除以 32 位除数的无符号除数。
  • s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) — 64 位除数除以 32 位除数的有符号除法。
  • u64 div64_u64(u64 dividend, u64 divisor) — 64 位除数除以 64 位除数的无符号除法。
  • s64 div64_s64(s64 dividend, s64 divisor)(自 2.6.37 起) 签署了 64 位除数除以 64 位除数。
  • u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)(自 3.12.0 起) 64 位除数除以 64 位除数的无符号除法。
  • s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder)(截至 4.18-rc8 尚不存在) 64 位除数除以 64 位除数的符号除以余数。
  • div64_long(x,y)(自 3.4.0 起) 宏,用于将 64 位被除数除以 long int 除数(32 位或 64 位,具体取决于架构)。
  • div64_ul(x,y)(自 3.10.0 起) 宏用于将 64 位被除数除以 unsigned long int 除数(32 位或 64 位,具体取决于架构) .
  • u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder) — 64 位除数除以 32 位除数的无符号除法,通过重复从被除数中减去除数,得到余数(如果预计除数不会比除数大很多,则可能比常规除法更快)。李>

【讨论】:

    【解决方案2】:

    您可以在任何位计算机上划分任何大小的数字。唯一的区别是划分的方式。在本机处理 64 位整数的处理器上,它将是一条机器代码指令(我不知道任何没有硬件划分的 64 位处理器)。在具有较窄寄存器的处理器上,它将被转换为一系列机器代码指令或对划分这些 64 位数字的库函数的调用:

    uint64_t foo(uint64_t x, uint64_t y)
    {
        return x/y;
    }
    

    关于 amd64 指令集:

            mov     rax, rdi
            xor     edx, edx
            div     rsi
            ret
    

    关于 ia32 指令集:

    sub     esp, 12
    push    DWORD PTR [esp+28]
    push    DWORD PTR [esp+28]
    push    DWORD PTR [esp+28]
    push    DWORD PTR [esp+28]
    call    __udivdi3
    add     esp, 28
    ret
    

    【讨论】:

    • 但是__udivdi3在Linux内核中不存在。在 Linux 内核中,您的 foo 可以替换为 #include <linux/math64.h> 定义的 div64_u64
    • @IanAbbott 只是为了说明单机指令与函数调用的区别。
    • 好吧,只要你明确指出你写的 foo 函数不适合在 Linux 内核中使用。
    猜你喜欢
    • 2018-07-30
    • 2011-10-03
    • 2017-06-29
    • 2012-08-01
    • 2015-05-19
    • 1970-01-01
    • 1970-01-01
    • 2018-01-25
    • 2012-11-30
    相关资源
    最近更新 更多