BITS_TO_LONGS宏的作用

本文深入解析了Linux内核中BIT_TO_LONGS函数的作用和内部实现机制,通过实例代码解释了如何计算给定位数转换为长整型数所需的长整型数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[linux-3.0]

BITS_TO_LONGS

定义在:include/linux/bitops.h

#define BITS_PER_BYTE        8

#define BITS_TO_LONGS(nr)    DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

DIV_ROUND_UP

定义在:include/linux/kernel.h (line 58)

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

其目的就是求一个数是几个long的长度。

例:sizeof(long) = 4;

BITS_TO_LONGS(31) = 1

BITS_TO_LONGS(32) = 1

BITS_TO_LONGS(33) = 2


The following is the test code:

#include <stdio.h>
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE           8
#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

main()
{
        int j = 32; 
        printf("The size of long is %ld\n", sizeof(long));
        printf("The convertion result is %ld\n", BITS_TO_LONGS(j));
}

/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_TYPES_H #define _LINUX_TYPES_H #define __EXPORTED_HEADERS__ #include <uapi/linux/types.h> #ifndef __ASSEMBLY__ #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] typedef u32 __kernel_dev_t; typedef __kernel_fd_set fd_set; typedef __kernel_dev_t dev_t; typedef __kernel_ulong_t ino_t; typedef __kernel_mode_t mode_t; typedef unsigned short umode_t; typedef u32 nlink_t; typedef __kernel_off_t off_t; typedef __kernel_pid_t pid_t; typedef __kernel_daddr_t daddr_t; typedef __kernel_key_t key_t; typedef __kernel_suseconds_t suseconds_t; typedef __kernel_timer_t timer_t; typedef __kernel_clockid_t clockid_t; typedef __kernel_mqd_t mqd_t; typedef _Bool bool; typedef __kernel_uid32_t uid_t; typedef __kernel_gid32_t gid_t; typedef __kernel_uid16_t uid16_t; typedef __kernel_gid16_t gid16_t; typedef unsigned long uintptr_t; #ifdef CONFIG_HAVE_UID16 /* This is defined by include/asm-{arch}/posix_types.h */ typedef __kernel_old_uid_t old_uid_t; typedef __kernel_old_gid_t old_gid_t; #endif /* CONFIG_UID16 */ #if defined(__GNUC__) typedef __kernel_loff_t loff_t; #endif /* * The following typedefs are also protected by individual ifdefs for * historical reasons: */ #ifndef _SIZE_T #define _SIZE_T typedef __kernel_size_t size_t; #endif #ifndef _SSIZE_T #define _SSIZE_T typedef __kernel_ssize_t ssize_t; #endif #ifndef _PTRDIFF_T #define _PTRDIFF_T typedef __kernel_ptrdiff_t ptrdiff_t; #endif #ifndef _CLOCK_T #define _CLOCK_T typedef __kernel_clock_t clock_t; #endif #ifndef _CADDR_T #define _CADDR_T typedef __kernel_caddr_t caddr_t; #endif /* bsd */ typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned long u_long; /* sysv */ typedef unsigned char unchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; #ifndef __BIT_TYPES_DEFINED__ #define __BIT_TYPES_DEFINED__ typedef u8 u_int8_t; typedef s8 int8_t; typedef u16 u_int16_t; typedef s16 int16_t; typedef u32 u_int32_t; typedef s32 int32_t; #endif /* !(__BIT_TYPES_DEFINED__) */ typedef u8 uint8_t; typedef u16 uint16_t; typedef u32 uint32_t; #if defined(__GNUC__) typedef u64 uint64_t; typedef u64 u_int64_t; typedef s64 int64_t; #endif /* this is a special 64bit data type that is 8-byte aligned */ #define aligned_u64 __aligned_u64 #define aligned_be64 __aligned_be64 #define aligned_le64 __aligned_le64 /** * The type used for indexing onto a disc or disc partition. * * Linux always considers sectors to be 512 bytes long independently * of the devices real block size. * * blkcnt_t is the type of the inode's block count. */ typedef u64 sector_t; typedef u64 blkcnt_t; /* * The type of an index into the pagecache. */ #define pgoff_t unsigned long /* * A dma_addr_t can hold any valid DMA address, i.e., any address returned * by the DMA API. * * If the DMA API only uses 32-bit addresses, dma_addr_t need only be 32 * bits wide. Bus addresses, e.g., PCI BARs, may be wider than 32 bits, * but drivers do memory-mapped I/O to ioremapped kernel virtual addresses, * so they don't care about the size of the actual bus addresses. */ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT typedef u64 dma_addr_t; #else typedef u32 dma_addr_t; #endif typedef unsigned int __bitwise gfp_t; typedef unsigned int __bitwise slab_flags_t; typedef unsigned int __bitwise fmode_t; #ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif typedef phys_addr_t resource_size_t; /* * This type is the placeholder for a hardware interrupt number. It has to be * big enough to enclose whatever representation is used by a given platform. */ typedef unsigned long irq_hw_number_t; typedef struct { int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } #ifdef CONFIG_64BIT typedef struct { s64 counter; } atomic64_t; #endif struct list_head { struct list_head *next, *prev; }; struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; struct ustat { __kernel_daddr_t f_tfree; #ifdef CONFIG_ARCH_32BIT_USTAT_F_TINODE unsigned int f_tinode; #else unsigned long f_tinode; #endif char f_fname[6]; char f_fpack[6]; }; /** * struct callback_head - callback structure for use with RCU and task_work * @next: next update requests in a list * @func: actual update function to call after the grace period. * * The struct is aligned to size of pointer. On most architectures it happens * naturally due ABI requirements, but some architectures (like CRIS) have * weird ABI and we need to ask it explicitly. * * The alignment is required to guarantee that bit 0 of @next will be * clear under normal conditions -- as long as we use call_rcu() or * call_srcu() to queue the callback. * * This guarantee is important for few reasons: * - future call_rcu_lazy() will make use of lower bits in the pointer; * - the structure shares storage space in struct page with @compound_head, * which encode PageTail() in bit 0. The guarantee is needed to avoid * false-positive PageTail(). */ struct callback_head { struct callback_head *next; void (*func)(struct callback_head *head); } __attribute__((aligned(sizeof(void *)))); #define rcu_head callback_head typedef void (*rcu_callback_t)(struct rcu_head *head); typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func); typedef void (*swap_func_t)(void *a, void *b, int size); typedef int (*cmp_r_func_t)(const void *a, const void *b, const void *priv); typedef int (*cmp_func_t)(const void *a, const void *b); #endif /* __ASSEMBLY__ */ #endif /* _LINUX_TYPES_H */
最新发布
08-09
<think>我们正在分析Linux内核中的`linux/types.h`头文件。这个头文件定义了大量的基本类型和结构,是内核开发的基础。下面是对该头文件的详细解析。 ### 头文件结构解析 1. **许可证标识**: ```c /* SPDX-License-Identifier: GPL-2.0 */ ``` 表明此文件遵循GPL-2.0许可证。 2. **头文件保护**: ```c #ifndef _LINUX_TYPES_H #define _LINUX_TYPES_H ... #endif /* _LINUX_TYPES_H */ ``` 防止重复包含。 3. **包含用户API头文件**: ```c #define __EXPORTED_HEADERS__ #include <uapi/linux/types.h> ``` 通过定义`__EXPORTED_HEADERS__`,然后包含用户空间API的头文件`uapi/linux/types.h`,这样可以从内核向用户空间导出必要的类型定义。 4. **条件编译`#ifndef __ASSEMBLY__`**: 这个头文件的大部分内容只有在不是汇编代码(`__ASSEMBLY__`未定义)时才有效。因为汇编代码不需要这些类型定义。 ### 类型定义详解 #### 1. **位图声明**: ```c #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] ``` 用于声明一个位图数组,数组大小由`bits`决定,通过`BITS_TO_LONGS`计算所需的长整型个数。 #### 2. **基本类型别名**: 头文件为内核中常用的类型定义了别名,例如: ```c typedef __kernel_dev_t dev_t; typedef __kernel_ino_t ino_t; typedef __kernel_mode_t mode_t; ... ``` 这些类型通常由架构特定的头文件(如`asm/posix_types.h`)定义。 #### 3. **布尔类型**: ```c typedef _Bool bool; ``` 使用C99标准的`_Bool`来定义`bool`类型。 #### 4. **用户和组ID类型**: 根据配置选项,定义了16位和32位的用户ID和组ID类型: ```c typedef __kernel_uid32_t uid_t; typedef __kernel_gid32_t gid_t; typedef __kernel_uid16_t uid16_t; typedef __kernel_gid16_t gid16_t; ``` 如果配置了`CONFIG_HAVE_UID16`,还会定义旧的16位ID类型。 #### 5. **指针类型**: ```c typedef unsigned long uintptr_t; ``` 用于存储指针值的无符号整数类型。 #### 6. **标准C类型**: 头文件定义了与标准C库兼容的类型,如`size_t`、`ssize_t`、`ptrdiff_t`等: ```c typedef __kernel_size_t size_t; typedef __kernel_ssize_t ssize_t; typedef __kernel_ptrdiff_t ptrdiff_t; ``` #### 7. **BSD和SysV风格的无符号类型**: 为了兼容性,定义了BSD风格(`u_char`、`u_short`等)和SysV风格(`unchar`、`ushort`等)的类型别名。 #### 8. **固定宽度整数类型**: 定义了固定宽度的整数类型,如`uint8_t`、`int8_t`等,这些类型在需要精确控制数据大小时使用。 #### 9. **对齐的64位类型**: ```c #define aligned_u64 __aligned_u64 ``` 用于需要64位对齐的数据类型。 #### 10. **磁盘和文件系统相关类型**: ```c typedef u64 sector_t; typedef u64 blkcnt_t; ``` `sector_t`用于表示磁盘扇区号,`blkcnt_t`用于表示块数量。 #### 11. **页缓存索引类型**: ```c #define pgoff_t unsigned long ``` 表示页面缓存中的偏移。 #### 12. **DMA地址类型**: 根据配置,DMA地址可以是32位或64位: ```c #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT typedef u64 dma_addr_t; #else typedef u32 dma_addr_t; #endif ``` #### 13. **标志位类型**: ```c typedef unsigned int __bitwise gfp_t; typedef unsigned int __bitwise slab_flags_t; typedef unsigned int __bitwise fmode_t; ``` 这些类型用于内存分配标志(gfp_t)、slab分配器标志(slab_flags_t)和文件模式标志(fmode_t)。`__bitwise`用于指示这些类型应该进行严格的类型检查。 #### 14. **物理地址类型**: ```c #ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif ``` 物理地址类型,根据配置可以是32位或64位。 #### 15. **资源大小类型**: ```c typedef phys_addr_t resource_size_t; ``` 用于表示资源(如I/O内存区域)的大小。 #### 16. **硬件中断号类型**: ```c typedef unsigned long irq_hw_number_t; ``` 用于保存硬件中断号。 #### 17. **原子类型**: ```c typedef struct { int counter; } atomic_t; ``` 原子操作的计数器。64位系统下还有`atomic64_t`。 #### 18. **链表结构**: ```c struct list_head { struct list_head *next, *prev; }; struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; ``` 定义了双向链表(`list_head`)和哈希链表(`hlist_head`和`hlist_node`)结构。 #### 19. **文件系统统计结构**: ```c struct ustat { ... }; ``` 用于文件系统统计信息。 #### 20. **回调函数头结构**: ```c struct callback_head { struct callback_head *next; void (*func)(struct callback_head *head); } __attribute__((aligned(sizeof(void *)))); ``` 用于RCU和任务工作(task_work)的回调结构。特别注意其对齐属性,这保证了指针的最低有效位为0(在正常条件下),这对于避免与PageTail标志冲突非常重要。 #### 21. **RCU相关类型定义**: ```c typedef void (*rcu_callback_t)(struct rcu_head *head); typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func); ``` 定义了RCU回调函数的类型。 #### 22. **排序和交换函数类型**: ```c typedef void (*swap_func_t)(void *a, void *b, int size); typedef int (*cmp_r_func_t)(const void *a, const void *b, const void *priv); typedef int (*cmp_func_t)(const void *a, const void *b); ``` 用于排序算法的交换函数和比较函数类型。 ### 关键点总结 - **跨平台兼容性**:通过条件编译(如`#ifdef CONFIG_...`)支持不同架构和配置。 - **用户空间导出**:通过包含`uapi/linux/types.h`向用户空间导出类型。 - **类型安全**:使用`__bitwise`等来增强类型检查。 - **数据结构**:提供了内核中常用的数据结构(如链表、原子操作、回调等)。 - **内存对齐**:特别注意对齐要求(如`callback_head`结构)。 ### 实际应用示例 1. **声明一个位图**: ```c DECLARE_BITMAP(my_bitmap, 128); // 声明一个128位的位图 ``` 2. **使用原子变量**: ```c atomic_t counter = ATOMIC_INIT(0); atomic_inc(&counter); ``` 3. **初始化链表头**: ```c struct list_head my_list = LIST_HEAD_INIT(my_list); ``` 4. **使用RCU回调**: ```c struct callback_head my_callback = { .next = NULL, .func = my_callback_function, }; ``` §§相关问题§§ 1. 为什么在`struct callback_head`中需要显式指定对齐属性? 2. 如何在内核模块中正确使用`DECLARE_BITMAP`? 3. 在哪些情况下需要使用`__bitwise`关键字?它对编译过程有什么影响? 4. 为什么`atomic_t`结构体只包含一个`int`类型的成员? 5. 在64位系统上,`dma_addr_t`和`phys_addr_t`是否总是相同的大小?为什么?</think>### Linux内核头文件 `types.h` 深度解析 该文件是Linux内核的核心类型定义头文件,定义了内核开发所需的基础数据类型和数据结构。以下是关键内容解析: --- #### 1. **基础架构设计** ```c #define __EXPORTED_HEADERS__ // 标志这些定义可导出到用户空间 #include <uapi/linux/types.h> // 包含用户空间可见的类型定义 #ifndef __ASSEMBLY__ // 仅对C代码有效(非汇编代码) ``` - **用户/内核空间桥梁**:通过`uapi`目录实现类型定义的双向共享 - **汇编隔离**:`__ASSEMBLY__`确保类型定义仅在C代码中生效 --- #### 2. **核心类型定义** | 类别 | 典型定义 | 说明 | |------|----------|------| | **设备相关** | `typedef __kernel_dev_t dev_t;` | 设备号类型 | | **文件系统** | `typedef __kernel_ino_t ino_t;` | inode编号类型 | | **进程管理** | `typedef __kernel_pid_t pid_t;` | 进程ID类型 | | **权限控制** | `typedef __kernel_mode_t mode_t;` | 文件权限类型 | | **用户/组** | `typedef __kernel_uid32_t uid_t;` | 用户ID类型 | | **内存指针** | `typedef unsigned long uintptr_t;` | 保证存下指针的整数类型 | --- #### 3. **特殊类型设计** ```c /* 原子操作类型 */ typedef struct { int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } // 原子变量初始化 /* 64位原子操作 */ #ifdef CONFIG_64BIT typedef struct { s64 counter; } atomic64_t; #endif /* 位图声明 */ #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] ``` --- #### 4. **关键数据结构** **双向链表**: ```c struct list_head { struct list_head *next, *prev; // 经典环形链表设计 }; ``` **哈希链表**: ```c struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; // 单指针表头节省内存,适用于哈希桶 ``` **RCU回调机制**: ```c struct callback_head { struct callback_head *next; void (*func)(struct callback_head *head); } __attribute__((aligned(sizeof(void *)))); // 强制指针对齐 ``` --- #### 5. **平台相关类型** ```c /* DMA地址类型 */ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT typedef u64 dma_addr_t; // 64位系统 #else typedef u32 dma_addr_t; // 32位系统 #endif /* 物理地址类型 */ #ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif ``` --- #### 6. **位级类型安全** ```c typedef unsigned int __bitwise gfp_t; // 内存分配标志 typedef unsigned int __bitwise slab_flags_t;// slab分配器标志 typedef unsigned int __bitwise fmode_t; // 文件打开模式 ``` - **`__bitwise`关键字**:强制编译器进行严格的类型检查 - **防止标志位误用**:不同类别的标志不可直接赋值 --- #### 7. **磁盘与文件系统类型** ```c typedef u64 sector_t; // 磁盘扇区号(固定512字节单位) typedef u64 blkcnt_t; // 块设备块数量 #define pgoff_t unsigned long // 页缓存索引类型 ``` --- ### 实际应用场景 **1. 设备驱动开发** ```c dev_t dev_id = MKDEV(major, minor); // 创建设备号 struct list_head device_list; // 设备链表 INIT_LIST_HEAD(&device_list); // 初始化链表 ``` **2. 内存分配** ```c gfp_t flags = GFP_KERNEL | __GFP_ZERO; // 页分配标志 void *page = alloc_pages(flags, order); // 按标志分配 ``` **3. RCU回调注册** ```c struct callback_head rcu_cb = { .func = my_rcu_callback }; call_rcu(&rcu_cb, rcu_callback_t); // 注册RCU回调 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值