Linux内核之32/64位除法


前言

本文主要是介绍 Linux 内核提供的关于32位与64位除法已经实现的接口,使得在 Linux 中需要除法运算或编译中遇到如上问题时能正确解决问题。


一、问题

在编译 Linux 代码时,经常会遇到如下错误:

	undefined symbol ‘udivdi3’
	undefined symbol 'divid3'

二、解决方法

1.根本原因描述

在嵌入式中,32位系统中(目前多数系统都是,比如 ARM),对于普通的 a 除以 b(b 为32位):

(1)当 a 为32位,Linux 内核中,常用 uint32_t 类型,可以直接写为 a / b;
(2)但是,对于 a 是64位,uint64_t 的时候,就要用到专门的除操作相关的函数,linux 内核里面一般为 do_div(n, base),注意,此处do_div得到的结果是余数,而真正的 a / b 的结果,是用 a 来保存的。

do_div(n,base) 的具体定义,和当前体系结构有关,所以其具体实现在相关架构目录下的 <asm/div64.h>,其实现较为复杂,我也没看懂。

因此,如果你当前写代码,a / b,如果 a 是uint64_t类型,那么一定要利用do_div(a,b),而得到结果 a,

而不能简单的用 a / b,否则编译可以正常编译,但是最后链接最后出错,会提示上面的那个错误:

undefined reference to “__udivdi3”

2.解决方法

知道导致该错误的根本原因之后,就可以在代码中找到对应的用到除法的地方
即类似于 a / b 的地方,其中被除数 a 为64位,Linux中一般用用 uint64_t,将a / bdo_div(a,b) 得到的 a 去代替(注意,不是直接用 do_div() 得到真正 a 除 b 后的结果,因为 do_div(a, b) 得到的是余数)

三、总结学习

1.do_div 函数

#define do_div(n, base) __do_div_asm(n, base)

#define __do_div_asm(n, base) [省略]

函数功能:

	实现在32位处理器中实现64位除法,需要包含头文件 <asm/div64.h>
	可以将其实现理解为:
	
	ret = n % base;
	n /= base;

函数参数:

	n:		uint64_t 的被除数
	base: 	32位的除数

函数返回值:
被除数除以除数的余数。

2.Linux 内核实现的64位除法函数

Linux 内核封装好了支持64位除法的函数,包含 unsigned 和 signed 两类,具体函数如下,需要包含头文件 < linux/math64.h >

#if BITS_PER_LONG == 32	//针对32位处理器

(1)div_u64

/* 
 * unsigned 64位除法,不需要的得到余数
 * Param - u64	: 被除数
 * Param - u32	: 除数
 * Return - u64	: 除后的结果
 */
static inline u64 div_u64(u64 dividend, u32 divisor)
{
	u32 remainder;
	return div_u64_rem(dividend, divisor, &remainder);
}

/* 
 * unsigned 64位除法,需要的得到余数
 * Param - u64	: 被除数
 * Param - u32	: 除数
 * Param - u32* : 除后的余数
 * Return - u64	: 除后的结果
 */
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
{
	*remainder = do_div(dividend, divisor);
	return dividend;
}

(2)div_s64

/* 
 * signed 64位除法,不需要的得到余数
 * Param - s64	: 被除数
 * Param - s32	: 除数
 * Return - s64	: 除后的结果
 */
static inline s64 div_s64(s64 dividend, s32 divisor)
{
	s32 remainder;
	return div_s64_rem(dividend, divisor, &remainder);
}

/* 
 * signed 64位除法,需要的得到余数
 * Param - s64	: 被除数
 * Param - s32	: 除数
 * Param - s32* : 除后的余数
 * Return - s64	: 除后的结果
 */
s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
{
	u64 quotient;

	if (dividend < 0) {
		quotient = div_u64_rem(-dividend, abs(divisor), (u32 *)remainder);
		*remainder = -*remainder;
		if (divisor > 0)
			quotient = -quotient;
	} else {
		quotient = div_u64_rem(dividend, abs(divisor), (u32 *)remainder);
		if (divisor < 0)
			quotient = -quotient;
	}
	return quotient;
}
EXPORT_SYMBOL(div_s64_rem);

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值