Linux内核--内核数据类型

转自:http://www.linuxidc.com/Linux/2013-12/93637.htm

将Linux 移植到新的体系结构时,开发者遇到的若干问题都与不正确的数据类型有关。坚持使用严格的数据类型和使用 -Wall -Wstrict-prototypes 进行编译可能避免大部分的 bug。

-Wall 显示所有的警告
-Wstrict-prototypes 严格的检测原型,如果不一致,则出现警告

内核数据使用的数据类型主要分为3个类型: 标准C语言类型、确定大小的类型和特定内核对象的类型。

标准 C 语言类型

当需要“一个2字节填充符”或“用一个4字节字串来代表某个东西”,就不能使用标准C语言类型,因为在不同的体系结构,C 语言的数据类型所占的空间大小不同。而且有的构架,内核空间和用户空间的C数据类型所占空间大小也可能不同。

内核中的地址是unsigned long类型,指针大小和long类型相同。
尽管概念上地址是指针,但使用一个无符号整型可以更好地实现内存管理; 内核把物理内存看成一个巨型数组, 内存地址就是该数组的索引。我们可以方便地对指针取值,但直接处理内存地址时,我们几乎从不会以这种方式对他取值。使用一个整数类型避免了这种取值,因此避免了bug。所以,利用至少在 Linux 目前支持的所有平台上,指针和长整型始终是相同大小的这一事实,内核中内存地址常常是unsigned long。

C99 标准定义了 intptr_t 和 uintptr_t 类型,它们是能够保存指针值的整型变量。但没在 2.6 内核中几乎没使用。

确定大小的类型

内核:当需要知道你定义的数据的大小时,可以使用内核提供的下列数据类型:

u8; /* unsigned byte (8 bits) */
u16; /* unsigned word (16 bits) */
u32; /* unsigned 32-bit value */
u64; /* unsigned 64-bit value */
/*虽然很少需要有符号类型,但是如果需要,只要用 s 代替 u*/

用户空间:若一个程序用户空间需要使用这些类型,可在符号前加一个双下划线: __u8和其它类型是独立于 __KERNEL__ 定义的。

接口特定的类型(_t 类型

内核中最常用的数据类型由它们自己的 typedef 声明,阻止了任何移植性问题。
“接口特定(interface-specific)”由某个库定义的一种数据类型, 以便为了某个特定的数据结构提供接口。
注意

近来已经很少定义新的接口特定的类型。有许多内核开发者已经不再喜欢使用 typedef 语句,他们宁愿看到代码中直接使用的真实类型信息。
很多老的接口特定类型在内核中保留,他们不会很快消失。
即使没有定义接口特定类型,也应该始终是用和内核其他部分保持一致、适当的数据类型。只要驱动使用了这种“定制”类型的函数,但又不遵照约定,编译器会发出警告,这时使用 -Wall 编译器选项并小心去除所有的警告,就可以确信代码的可移植性了。

_t 类型的主要问题


打印它们时,常常不容易选择正确的 printk 或 printf 格式。

打印接口特定的数据的最好方法是:将其强制转换为可能的最大类型(常常是 long 或 unsigned long ) 并用相应的格式打印。

时间间隔

当处理时间间隔时,不要假定每秒的jiffies个数,不是每个 Linux 平台都以固定的速度运行。当计算时间间隔时,要使用 HZ ( 每秒的定时器中断数 ) 来标定你的时间。


Hz:Linux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。举例来说,HZ为1000,代表每秒有1000次timer interrupts。

Tick:Tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒(millisecond)。

Jiffies:Jiffies为Linux核心变数(unsigned long),它被用来记录系统自开机以来,已经过了多少tick。每发生一次timer interrupt,Jiffies变数会被加一。

页大小

当使用内存时,记住一个内存页是 PAGE_SIZE 字节, 不是 4KB。相关的宏定义是 PAGE_SIZE 和 PAGE_SHIFT(包含将一个地址移位来获得它的页号的位数)。如果用户空间程序需要这些信息,可以使用 getpagesize 库函数。
若一个驱动需要 16 KB 来暂存数据,一个可移植得解决方法是 get_order:

#include <asm/page.h>
int order = get_order(16*1024);
buf = get_free_pages(GFP_KERNEL, order);
/*get_order 的参数必须是 2 的幂*/

字节存储顺序

不要假设字节序。 代码应该编写成不依赖所操作数据的字节序的方式。
头文件定义:

#ifdef __ARMEB__
#include linux/byteorder/big_endian.h>
#else
#include linux/byteorder/little_endian.h>
#endif

在<linux/byteorder/big_endian.h>中定义了__BIG_ENDIAN ,

而在<linux/byteorder/little_endian.h>中定义了__LITTLE_ENDIAN,

这些依赖处理器的字节序当处理字节序问题时,需要编码一堆类似

#ifdef __LITTTLE_ENDIAN 的条件语句。

但是还有一个更好的方法:Linux 内核有一套宏定义来处理处理器字节序和特定字节序之间的转换。例如:
u32 cpu_to_le32 (u32);
u32 le32_to_cpu (u32);

/*这些宏定义将一个CPU使用的值转换成一个无符号的32位小头数值,无论 CPU 是大端还是小端,也不管是不是32 位处理器。在没有转换工作需要做时,返回未修改的值。*/

数据对齐

编写可移植代码而值得考虑的最后一个问题是如何访问未对齐的数据。存取不对齐的数据应当使用下列宏:
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val, ptr);

这些宏是无类型的,并对各总数据项,不管是 1、2、4或 8 个字节,他们都有效,并且在所有内核版本中都有定义。

关于对齐的另一个问题是数据结构的跨平台移植性。同样的数据结构在不同的平台上可能被不同地编译。为了编写可以跨体系移植的数据结构,应当始终强制数据项的自然对齐。
自然对齐(natural alignment)指的是:数据项大小的整数倍的地址上存储数据项。 应当使用填充符避免强制自???对齐时编译器移动数据结构的字段,在数据结构中留下空洞。

为了目标处理器的良好性能,编译器可能悄悄地插入填充符到结构中,来保证每个成员是对齐的。若定义一个和设备要求的结构体相匹配结构,自动填充符会破坏这个意图。解决这个问题的方法是告诉编译器这个结构必须是"紧凑的", 不能增加填充符。例如下列的定义:


struct
{
u16 id;
u64 lun;
u16 reserved1;
u32 reserved2;
}
__attribute__ ((packed)) scsi;

/*如果在 64-位平台上编译这个结构,若没有 __attribute__ ((packed)), lun 成员可能在前面被添加 2 个或 6 个填充符字节。指针和错误值*/

可以在利用ARM9和USB摄像头进行视频采集的servfox源代码的spcaframe.h头文件中找到这种方法的实际应用:

struct frame_t{
char header[5];
int nbframe;
double seqtimes;
int deltatimes;
int w;
int h;
int size;
int format;
unsigned short bright;
unsigned short contrast;
unsigned short colors;
unsigned short exposure;
unsigned char wakeup;
int acknowledge;
} __attribute__ ((packed));
struct client_t{
char message[4];
unsigned char x;
unsigned char y;
unsigned char fps;
unsigned char updobright;
unsigned char updocontrast;
unsigned char updocolors;
unsigned char updoexposure;
unsigned char updosize;
unsigned char sleepon;
} __attribute__ ((packed));

Linux Kernel 的详细介绍请点这里
Linux Kernel 的下载地址请点这里

相关阅读

Linux内核将用nftables替代iptables http://www.linuxidc.com/Linux/2013-10/91727.htm

Linux 3.12代号Suicidal Squirrel http://www.linuxidc.com/Linux/2013-09/90023.htm

怎样在 Ubuntu 上安装 Linux 3.11 内核 http://www.linuxidc.com/Linux/2013-09/89674.htm

Ubuntu 13.10 (Saucy Salamander) 内核已升级至 Linux Kernel 3.10 RC5 http://www.linuxidc.com/Linux/2013-06/86110.htm

Linux Kernel 3.4.62 LTS 现已经提供下载 http://www.linuxidc.com/Linux/2013-09/90368.htm

如何在Ubuntu 13.10上安装Linux内核 3.12 http://www.linuxidc.com/Linux/2013-11/92930.htm

时间: 2024-10-25 06:12:36

Linux内核--内核数据类型的相关文章

自制linux和内核编译

自制linux和内核编译 1.分区并创建文件系统 fdisk /dev/sdb分两个必要的分区/dev/sdb1对应/boot /dev/sdb2对应根/mkfs.ext4 /dev/sdb1mkfs.ext4 /dev/sdb2 2.挂载boot mkdir/mnt/bootmount /dev/sdb1 /mnt/boot 3.安装grub grub-install --root-directory=/mnt  /dev/sdb 4.建立grub.conf: vim /mnt/boot/gr

Linux大脑 &quot;内核&quot; 内核编译(NTFS)

Linux大脑 "内核" 关于它 什么是内核 kernel(内核)是操作系统的核心,相当于人的大脑,掌控所有的硬件设备的控制权,也就是希望计算机帮你完成各项工作,那都需要通过内核的帮助才能完成,当然,如果你想实现的功能内核没有提供,那就必须添加相关的模块到内核中,就类似驱动程序,有了模块的支持我们计算机才能去操控硬件,完成我们想完成的工作 内核其实就是系统上面的一个文件,这个文件包含了驱动硬件的检测程序与驱动模块,内核文件在/boot目录下一个以vmlinuz开头的文件,有时候/boo

【转】Linux CentOS内核编译:下载CentOS源码、编译2.6.32-220的错误(apic.c:819 error &#39;numi_watchdog&#39; undeclared)

一.下载CentOS源码 1.1 查看CentOS版本 cat /etc/issue 1.2 查看Linux内核版本 uname -r 1.3 下载 文件名:kernel-2.6.32-220.el6.src.rpm 下载地址:http://vault.centos.org/6.2/os/Source/SPackages/ 官网:http://vault.centos.org/ 1.4 从kernel-2.6.32-220.el6.src.rpm获取源码 1. rpm -i kernel-2.6

Linux Bluetooth内核分析之HCI部分

关于HCI规范相关内容,请看<Bluetooth HCI介绍> 首先我们要了解,在Linux上实现的是HCI的Host部分 1. 相关数据结构 1.1 hci_dev 在Linux中hci_dev用来表示一个HCI Host(对应于一个Control) 成员 作用 char name[8] 蓝牙名称 __u8 bus HCI总线类型,有HCI_USB,HCI_PCCARD,HCI_UART,HCI_PCI等 __u8 dev_type HCI Controller类型,有HCI_BREDR,H

Linux进程/内核模型

内核必须实现一组服务和相应的接口,应用程序则可以使用这些接口,而不是直接与硬件打交道. Linux内核主要由以下5个子系统组成:进程调度.内存管理.虚拟文件系统.进程间通信以及设备驱动. 在这个组成中,最核心的就是进程管理->进程调度和进程间通信. 在Linux系统中,我们编写的任何应用层程序,不管是上层还是属于中间框架层的代码,甚至是最底层的驱动代码,都可以以进程的形式在系统上运行.CPU可以运行在用户态和内核态. 在Linux 进程/内核模型中,每个进程就是执行在机器上的唯一的镜像,它们对系

[linux内核]linux各个内核配置选项的含义以及配置

1,linux各个内核配置选项的含义 linux各个内核配置选项含义 2,make menuconfig命令的使用 Y表示加载,N表示不加载,M表示的是作为模块的方式载入内核. 3,以模块方式载入的时候如何动态加载 如何动态加载模块

[Linux内存]——内核地址空间

一,为什么需要高端内存答:对于32位机器,linux虚拟内存最大为4G,其中3-4G空间是用作内核空间,考虑到如果物理内存大于1G,那么物理内存不能得到完全的映射, 因此,Linux 规定“内核直接映射空间” 最多映射 896M 物理内存,ARM体系架构上有高端内存的概念,不过不是固定在896M以上的区域~ 二,linux内核地址空间 linux虚拟地址3G到4G的空间为内核地址空间,内核空间是由内核负责映射,他并不会跟着进程改变,是固定的. 1,3G---3G+896M是直接映射区,该区域的线

虚拟化–操作系统级 LXC Linux Containers内核轻量级虚拟化技术

友情提示:非原文链接可能会影响您的阅读体验,欢迎查看原文.(http://blog.geekcome.com) 原文地址:http://blog.geekcome.com/archives/288 软件平台:Ubuntu 14.04 容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好的在孤立的组之间有冲突的资源使用需求.与其他的虚拟化比较,这样既不需要指令级模拟,也不需要即时编译.容器可以在寒心CPU本地运行指令,而不需要任何专门的解释机制.此外半虚拟化和系统调用替换的复杂性. LXC

linux中内核的一个不错的参数somaxconn

导读:在linux中,/proc/sys/net/core/somaxconn这个参数,linux中内核的一个不错的参数somaxconn 看下其解析: 对于一个TCP连接,Server与Client需要通过三次握手来建立网络连接.当三次握手成功后, 我们可以看到端口的状态由LISTEN转变为ESTABLISHED,接着这条链路上就可以开始传送数据了. 每一个处于监听(Listen)状态的端口,都有自己的监听队列.监听队列的长度,与如下两方面有关: - somaxconn参数. - 使用该端口的

(转)linux IO 内核参数调优 之 参数调节和场景分析

1. pdflush刷新脏数据条件 (linux IO 内核参数调优 之 原理和参数介绍)上一章节讲述了IO内核调优介个重要参数参数. 总结可知cached中的脏数据满足如下几个条件中一个或者多个的时候就会被pdflush刷新到磁盘: (1)数据存在的时间超过了dirty_expire_centisecs(默认30s)时间 (2)脏数据所占内存 /(MemFree + Cached - Mapped) > dirty_background_ratio.也就是说当脏数据所占用的内存占(MemFre