跳到主要内容

通用宏定义 (xdef)

libx/xdef.h 提供了 XSTAR 全局通用的基础宏定义和工具函数,包括常用尺寸常量、布尔/空指针定义、容器与偏移计算、分支预测提示、静态断言、数学辅助宏,以及位扫描与原子操作内联函数。该头文件完全自包含——不引入任何其他头文件(包括 xstarcfg.h),也不依赖具体平台,所有源码均可包含。

尺寸常量​

以十六进制形式预定义的常用内存尺寸常量,便于书写缓冲区大小、页面对齐、堆/栈尺寸等。

宏值含义
SZ_160x0000001016 字节
SZ_2560x00000100256 字节
SZ_5120x00000200512 字节
SZ_1K0x000004001 KiB
SZ_4K0x000010004 KiB
SZ_8K0x000020008 KiB
SZ_16K0x0000400016 KiB
SZ_32K0x0000800032 KiB
SZ_64K0x0001000064 KiB
SZ_128K0x00020000128 KiB
SZ_256K0x00040000256 KiB
SZ_512K0x00080000512 KiB
SZ_1M0x001000001 MiB
SZ_2M0x002000002 MiB
SZ_4M0x004000004 MiB
SZ_8M0x008000008 MiB
SZ_16M0x0100000016 MiB
SZ_32M0x0200000032 MiB
SZ_64M0x0400000064 MiB
SZ_128M0x08000000128 MiB
SZ_256M0x10000000256 MiB
SZ_512M0x20000000512 MiB
SZ_1G0x400000001 GiB
SZ_2G0x800000002 GiB

示例:

char buf[SZ_4K];
void * heap = xos_mem_malloc(SZ_1M);

基础常量​

NULL​

空指针常量。C++ 环境下定义为 0,C 环境下定义为 ((void *)0)。仅在编译器未预先提供时定义。

TRUE / FALSE​

布尔真假常量,分别为 1 和 0。XSTAR 内部统一使用这两个宏来表示布尔结果,避免引入 <stdbool.h> 依赖。

if(register_driver(&drv) == FALSE)
return FALSE;
return TRUE;

结构与指针操作​

offsetof(type, member)​

计算结构体成员相对于结构体起始位置的字节偏移。GCC 4 及以上版本使用编译器内建 __builtin_offsetof,否则使用经典宏定义。

struct foo_t {
int a;
char b;
void * c;
};

size_t off = offsetof(struct foo_t, c);

container_of(ptr, type, member)​

由结构体成员的地址反推得到包含它的结构体的地址。Linux 内核风格的经典宏,是 XSTAR 中实现侵入式数据结构(如 list_head_t、hlist_node_t、rb_node_t 等)的基础。

struct list_head_t * pos;
struct my_node_t * node = container_of(pos, struct my_node_t, list);

分支预测​

likely(expr) / unlikely(expr)​

提示编译器条件表达式的预期结果,便于优化指令布局。GCC 3 及以上版本使用 __builtin_expect,其他编译器退化为普通的布尔表达式。

if(unlikely(ptr == NULL))
return FALSE;

if(likely(count > 0))
do_work();

仅在性能敏感且分支倾向非常明显的热路径上使用,普通代码不需要。

静态断言​

STATIC_ASSERT(cond)​

编译期断言,条件为真时不产生任何代码,条件为假时通过定义大小为 -1 的数组触发编译错误。常用于约束结构体大小、枚举范围等。

STATIC_ASSERT(sizeof(uint32_t) == 4);
STATIC_ASSERT(DEVICE_TYPE_MAX_COUNT < 256);

数学与数值工具​

XMAP(x, ia, ib, oa, ob)​

将 x 从输入区间 [ia, ib] 线性映射到输出区间 [oa, ob],常用于 ADC 采样、亮度/音量归一化等。

int duty = XMAP(adc_value, 0, 4095, 0, 100);

XMIN(a, b) / XMAX(a, b)​

类型安全的最小值/最大值宏。使用 GCC 语句表达式与 typeof,避免参数副作用,并在类型不一致时给出编译告警。

int m = XMIN(a, b);
int n = XMAX(x + 1, y - 1);

XCLAMP(v, a, b)​

将 v 限制到闭区间 [a, b],等价于 XMIN(XMAX(a, v), b)。

int volume = XCLAMP(user_input, 0, 100);

XFLOOR(x) / XROUND(x) / XCEIL(x)​

浮点向 int 取整宏,分别实现向下取整、四舍五入、向上取整。无需依赖 <math.h>,正负数处理对称。

int a = XFLOOR(1.7); /* 1 */
int b = XROUND(1.5); /* 2 */
int c = XCEIL(-1.2); /* -1 */

XDIV255(x)​

快速将整数除以 255,等价于 (x + 1) * 257 >> 16。常用于 8-bit 颜色通道的 alpha 混合计算,比常规除法更快。

uint8_t blended = XDIV255((uint32_t)src * alpha);

XBYTEMUL(x, a)​

将 32 位整数中两组交错的 8-bit 字节通道(如 RGBA 的 R/B 与 G/A)同时乘以 8-bit 常量 a 并归一化到 8-bit,是图形子系统中颜色乘法的核心优化宏。

数组与字符串​

ARRAY_SIZE(array)​

获取静态数组的元素个数。仅适用于真正的数组对象(非指针)。

static const char * names[] = { "a", "b", "c" };
for(int i = 0; i < ARRAY_SIZE(names); i++)
use(names[i]);

X(...)​

将可变参数原样转换为字符串字面量,常用于在不引入额外引号的情况下嵌入 JSON、设备树片段等多行文本。

const char * json = X({
"name": "demo",
"value": 42
});

位扫描内联函数​

下列内联函数均使用 GCC 内建指令实现,零开销。

int xffs(int x)​

返回 x 中最低位 1 的位置(从 1 开始计数),x 为 0 时返回 0。

int xfls(int x)​

返回 x 中最高位 1 的位置(从 1 开始计数),x 为 0 时返回 0。常用于计算以 2 为底的对数、向上取 2 的整数次幂。

int order = xfls(size - 1); /* 求最小 2^order >= size */

unsigned long __xffs(unsigned long x)​

返回 x 中最低位 1 的位下标(从 0 开始计数),等价于 __builtin_ctzl。x 不应为 0。

unsigned long __xfls(unsigned long x)​

返回 x 中最高位 1 的位下标(从 0 开始计数),等价于 (sizeof(long) * 8) - 1 - __builtin_clzl(x)。x 不应为 0。

unsigned long bit = __xffs(mask); /* 取出最低有效位下标 */
mask &= ~(1UL << bit); /* 清除该位 */

原子操作内联函数​

基于 GCC __atomic 系列编译器内建实现的 int 宽度整数原子操作。所有函数使用 always_inline 强制内联,即使在 -O0 优化级别下也没有函数调用开销;目标架构存在原生指令时直接编译为单条指令(x86 lock 前缀、ARMv7 ldrex/strex、AArch64 ldxr/stxr、RISC-V amo/lr.w/sc.w),不依赖任何运行时库。

API 分为两档:默认 6 个函数统一采用顺序一致性(seq_cst)语义;另有 4 个以内存序命名的弱序变体(acquire/release/relaxed),仅供性能敏感的热点路径使用。

操作对象为 struct xatomic_t——包装了单个 volatile int(所有支持平台上均为 32 位)的结构体类型。结构体包装带来编译期类型隔离:普通 int * / volatile int * 无法静默传入本 API,"同一变量一处普通访问、一处原子访问"这类隐蔽错误在编译期即暴露(Linux 内核 atomic_t 的同款做法)。调试检视时直接读取 .v 成员。不提供 64 位或子字(8/16 位)宽度——这类宽度在 RV32 等平台上会退化为 libatomic 库调用,违背零依赖原则。

int xatomic_load(const struct xatomic_t * p)​

原子读取 *p。参数带 const,const volatile int * 与普通 volatile int * 对象均可传入。

void xatomic_store(struct xatomic_t * p, int v)​

原子写入 v 到 *p。

int xatomic_add(struct xatomic_t * p, int v)​

原子加法 *p += v,返回加之前的旧值。适用于多线程计数器、引用计数。

int xatomic_sub(struct xatomic_t * p, int v)​

原子减法 *p -= v,返回减之前的旧值。与 xatomic_add 对称。

int xatomic_cas(struct xatomic_t * p, int o, int n)​

比较并交换:当 *p == o 时写入 n 并返回 1,否则返回 0。失败时不重试,需要循环的场景由调用方自行驱动。

int xatomic_xchg(struct xatomic_t * p, int n)​

无条件交换:写入 n 并返回旧值。在有原生指令的架构上映射为单条交换指令(x86 lock xchg、RISC-V amoswap),适合"抢锁/占用"模式。

int xatomic_load_acquire(const struct xatomic_t * p)​

acquire 语义的原子读取:该操作之后的访存不会被重排到它之前。作为 acquire/release 配对中的读端(例如先读到就绪标志、再访问其发布的数据),与 xatomic_store_release 成对使用。在 ARM/RISC-V 上比 xatomic_load 少一条内存屏障。

void xatomic_store_release(struct xatomic_t * p, int v)​

release 语义的原子写入:该操作之前的访存不会被重排到它之后。作为 acquire/release 配对中的写端(例如先写好数据、再发布就绪标志),与 xatomic_load_acquire 成对使用。

int xatomic_add_relaxed(struct xatomic_t * p, int v)​

relaxed 原子加法 *p += v,返回加之前的旧值。仅保证原子性,不提供与周围访存之间的顺序约束;适用于与前后代码无顺序依赖的纯统计计数。

int xatomic_sub_relaxed(struct xatomic_t * p, int v)​

relaxed 原子减法 *p -= v,返回减之前的旧值。与 xatomic_add_relaxed 对称。

内存序​

默认 6 个函数(load/store/add/sub/cas/xchg)统一使用 seq_cst(顺序一致性)语义——最难用错的默认档位。xatomic_cas 的失败路径按 C11 约束使用 relaxed(失败序不得强于成功序)。

本 API 不暴露通用内存序参数,以避免误用 relaxed/acquire 引入难以复现的并发缺陷;取而代之的是以语义命名的 4 个弱序变体,专供热点路径:

  • xatomic_load_acquire + xatomic_store_release:acquire/release 配对,用于自旋锁临界区边界、无锁环形缓冲等"发布/消费"场景;
  • xatomic_add_relaxed + xatomic_sub_relaxed:纯计数,只保证原子性。

更细粒度的内存序组合仍应在实现层直调 __atomic 内建,而不是继续扩展本 API。

注意事项​

  • 参数上的 volatile 修饰只是为了同时兼容 volatile int 与普通 int 两种声明方式,不提供任何同步语义;原子性与排序完全由编译器内建承担。
  • 原子操作仅适用于共享计数器、标志位、锁字等单一变量场景;涉及多变量的复合状态请使用 XOS 互斥锁(xos_mutex_lock 等)。
  • 操作对象统一为 struct xatomic_t,而非裸 volatile int:结构体包装让类型系统在编译期阻止普通指针混入,防住"同一变量一处普通访问、一处原子访问"的隐蔽错误;调试检视时读取 .v 成员即可。API 参数与返回值保持 int(与 Linux 内核 atomic_t 做法一致)。

使用示例​

static struct xatomic_t counter;

xatomic_add(&counter, 1); /* 多线程安全递增 */
int cur = xatomic_load(&counter); /* 原子读取快照 */

用 CAS 循环无锁地维护最大值:

static struct xatomic_t max_val;

void update_max(int v)
{
int o = xatomic_load(&max_val);
while(v > o)
{
if(xatomic_cas(&max_val, o, v))
break;
o = xatomic_load(&max_val); /* 竞争失败,取最新值重试 */
}
}

acquire/release 配对(发布/消费有序性):

/* 生产者:先写好数据,再发布就绪标志 */
xatomic_store_release(&ready, 1);

/* 消费者:acquire 读到标志后,数据必然已可见 */
if(xatomic_load_acquire(&ready))
use(payload);

使用建议​

  • 所有源文件应通过 #include <xstar.h> 或 #include <libx/xdef.h> 间接/直接引入这些定义。
  • 优先使用 XMIN/XMAX/XCLAMP 而不是手写三元表达式,以避免参数副作用导致的难查 Bug。
  • likely/unlikely 不要滥用;只有在确实需要优化指令布局的热点路径上才使用。
  • STATIC_ASSERT 在头文件中可放置在文件作用域;在函数内部使用时同样生效。
  • 与 XOS 抽象层、libx/xtypes.h 共同构成 XSTAR 源码所依赖的最小基础设施。