轉自:https://blog.csdn.net/eZiMu/article/details/55190206
本文將按照以下幾點描述:
基本數據類型
字節序
數據對齊
指針檢查
鏈表
基本數類型:
先看下面基本數據類型占用空間情況:
可以看出各體系CPU有差異,而使用u8,u16,u32,u64沒有差異。
因此,我們在定義基本數據類型時,要比較清楚一個類型站有幾個字節,盡量養成用內核定義的,類似u32這種符號。當然,sizeof()給定一個變量,就可以返回占用空間字節數。
在使用類似u32…的只需要包含頭文件:
#include <linux/types.h>
如在types.h里面定義的:
#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
或者int-ll64.h(被types.h包含進去了)
typedef signed char s8;
typedef unsigned char u8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed int s32;
typedef unsigned int u32;
typedef signed long long s64;
typedef unsigned long long u64;
以及:
typedef __u16 __bitwise __le16;//定義小端模式存儲16位數據類型
typedef __u16 __bitwise __be16;//定義大端模式存儲16位數據類型
typedef __u32 __bitwise __le32;//定義小端模式存儲32位數據類型
typedef __u32 __bitwise __be32;//定義大端模式存儲32位數據類型
typedef __u64 __bitwise __le64;//定義小端模式存儲64位數據類型
typedef __u64 __bitwise __be64;//定義大端模式存儲64位數據類型
還有其他,詳細看types.h文件。
字節序
字節序,就是指大端模式和小端模式。
當存取變量時,不知道是按照大端還是小端。那么我們先包含下面頭文件:
#include <asm/byteorder.h>
然后,有頭文件聲明的接口,如:
//將當前__u64轉換成,小端模式的64位(不管當前__u64是大端還是小端)
#define __cpu_to_le64(x) ((__force __le64)(__u64)(x))
//將小端模式__le64轉換成,當前__u64(不管當前__u64是大端還是小端)
#define __le64_to_cpu(x) ((__force __u64)(__le64)(x))
#define __cpu_to_le32(x) ((__force __le32)(__u32)(x))
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
#define __cpu_to_le16(x) ((__force __le16)(__u16)(x))
#define __le16_to_cpu(x) ((__force __u16)(__le16)(x))
#define __cpu_to_be64(x) ((__force __be64)__swab64((x)))
#define __be64_to_cpu(x) __swab64((__force __u64)(__be64)(x))
#define __cpu_to_be32(x) ((__force __be32)__swab32((x)))
#define __be32_to_cpu(x) __swab32((__force __u32)(__be32)(x))
#define __cpu_to_be16(x) ((__force __be16)__swab16((x)))
#define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x))
__le64 __cpu_to_le64p(const __u64 *p);//變量地址做為參數,來轉換值
__u64 __le64_to_cpup(const __le64 *p);
__le32 __cpu_to_le32p(const __u32 *p);
__u32 __le32_to_cpup(const __le32 *p);
__u32 __le32_to_cpup(const __le32 *p);
__le16 __cpu_to_le16p(const __u16 *p);
__u16 __le16_to_cpup(const __le16 *p);
__be64 __cpu_to_be64p(const __u64 *p);
__u64 __be64_to_cpup(const __be64 *p);
__be32 __cpu_to_be32p(const __u32 *p);
__u32 __be32_to_cpup(const __be32 *p);
__be16 __cpu_to_be16p(const __u16 *p);
__u16 __be16_to_cpup(const __be16 *p);
數據對齊:
數據對齊,是指字節對齊,字對齊等。
linux提供訪問各種數據對齊的統一接口。
用接口前,先包含頭文件:
#include <linux/unaligned.h>
接口:
get_unaligned(ptr);
put_unaligned(val, ptr);
ptr是指針,
返回指針指向空間的值。
如果ptr指針是按照字節對齊的,當然返回的是一個byte。
如果ptr指針是按照字對齊的,當然返回的是字,即4個byte。
指針檢查:
linux里面指針檢查,對驅動來說,就是檢查指針指向的數據地址,是否在內核空間。
有三個接口,先包含頭文件:
#include <linux/err.h>
接口:
void * __must_check ERR_PTR(long error);//error是錯誤碼,返回錯誤碼對應的錯誤地址
long __must_check PTR_ERR(const void *ptr);//ptr是錯誤地址,返回錯誤地址對應的錯誤碼
long __must_check IS_ERR(const void *ptr);//ptr是地址,用來判斷地址是否錯誤,錯誤返回1,沒有錯誤返回0
一般先用IS_ERR判斷是否有錯誤(這里錯誤,是指是否為內核空間地址),然后用PTR_ERR返回錯誤碼。如:
if(IS_ERR(P))
return PTR_ERR(P);
錯誤碼定義在include/linux/errno.h。
鏈表:
用linux鏈表前,包含頭文件:
#include <linux/list.h>
1
鏈表結構體:
struct list_head {
struct list_head *next, *prev;
};
1
2
3
一些接口:
//在緊接着鏈表 head 后面增加新入口項
list_add(struct list_head *new, struct list_head *head);
//在給定鏈表頭前面增加一個新入口項
list_add_tail(struct list_head *new, struct list_head *head);
//給定的項從隊列中去除,如果還需要用entry,就用list_del_init。
list_del(struct list_head *entry);
list_del_init(struct list_head *entry);
//將list刪除,然后加入到head后面
list_move(struct list_head *list, struct list_head *head);
//將list刪除,然后加入到head前面
list_move_tail(struct list_head *list, struct list_head *head);
//判斷head是否為空,為空返回1,不是返回0
list_empty(struct list_head *head);
//將 list 緊接在 head 之后來連接 2 個鏈表.
list_splice(struct list_head *list, struct list_head *head);
/*通過嵌入到結構體鏈表的地址(鏈表是結構體的一個成員),推算結構體首地址。list_entry其實也是用container_of來實現的*/
list_entry(struct list_head *ptr, type_of_struct, field_name);
//遍歷整個鏈表。這個宏創建一個 for 循環, 執行一次, cursor 指向鏈表中的每個連續的入口項.
list_for_each(struct list_head *cursor, struct list_head *list);
//是list_for_each反方向的遍歷
list_for_each_prev(struct list_head *cursor, struct list_head *list)
/*如果你的循環可能刪除列表中的項, 使用這個版本. 它簡單的存儲列表 next 中下
一個項, 在循環的開始, 因此如果 cursor 指向的入口項被刪除, 它不會被搞亂.list_for_each的安全版本*/
list_for_each_safe(struct list_head *cursor, struct list_head *next, struct
list_head *list)
//是list_entry和list_for_each的結合
list_for_each_entry(type *cursor, struct list_head *list, member)
//list_entry和list_for_each_safe的結合
list_for_each_entry_safe(type *cursor, type *next, struct list_head *list,
member)
用好鏈表,將結構體聯系在一起很方便,特別是list_entry,或者container_of。
使用鏈表前,先定義一個頭,並初始化,然后其他鏈表鏈接上來,只要又這個頭,連在上面的,用以上接口非常容易找到。
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
void INIT_LIST_HEAD(struct list_head *list);
————————————————
版權聲明:本文為CSDN博主「eZiMu」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/eZiMu/article/details/55190206