Linux设备驱动开发详解--笔记3--Linux内核及内核编程

在设备驱动方面,Linux2.6相对于Linux2.4有较大的改动,这主要表现在内核API中增加了不少新功能(如内存池)、sysfs文件系统、内核模块从.o变为.ko、驱动模块编译方式、模块使用计数、模块加载和卸载函数的定义等方面

 

Linux内核主要有进程调度、内存管理、虚拟文件系统、网络接口和进程间通信等5个子系统组成,如下图:

 

在设备驱动编程中,当请求的资源不能满足时,驱动一般会调度其他线程执行,并使驱动对应的进程进入睡眠状态,直到它请求的资源被释放,才会被唤醒而纪念日就绪状态

 

在设备驱动编程中,如果需要几个并发执行的任务,可以启动内核线程,启动内核线程的函数为:int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);

 

Linux内存管理完成为每个进程进行虚拟内存到物理内存的转换。一般而言,Linux的每个进程享有4GB的内存空间,0-3GB属于用户空间,3-4GB属于内核空间,内核空间对常规内存、I/O设备内存以及高端内存存在不同的处理方式

 

虚拟文件系统隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口。而且,它独立于各个具体的文件系统,是对各种文件系统的一个抽象,它使用super block存放文件系统相关信息,使用索引节点inode存放文件的物理信息,使用目录项dentry存放文件的逻辑信息

 

Linux系统只能通过系统调用和硬件中断完成从用户空间到内核空间的控制转移

 

Linux下的c编程

 

1、零长度数组

GNU C中允许使用零长度的数组。它们作为结构体的最后一个元素十分有用,在ISO C89中,你需要给目录分配长度1,意味着要浪费空间或者使malloc的参数变得复杂。在ISO C99中,你可以使用灵活的数组元素,只是在语法和主义上有微小的差异。

·灵活的数组元素写为contents[]不带0。

·灵活的数组元素具有不完全的类型,所以sizeof操作也许不能被应用。作为零长度数组的原始实现的奇怪之处,sizeof被赋值为0。

 

灵活的数组元素可以作为结构体的最后一个元素出现当其它元素非空时。GCC现在在任何地方允许零长度数组。定义仅含有一个零长度数组的结构体,无论如何,你可能会遇到问题。这样的用法被反对,我们建议仅当灵活数组元素被允许的地方使用零长度数组。如:

 

#include <stdio.h>

#include <string.h>

 

struct arr

{

       int a;

       char buff[0];

};

 

int main()

{

       char b[0];

       struct arr *ptr;

      

       ptr = (struct arr *)malloc(sizeof(struct arr)+4);

      

       if(ptr == NULL)

       {

       printf("malloc failed/n");

       return -1;

       }

      

       memset(ptr,0x30,sizeof(struct arr)+4);

      

       printf("0x%x 0x%x 0x%x 0x%x/n", ptr->buff[0],ptr->buff[1],ptr->buff[2],ptr->buff[3]);

      

       printf("%d %d %d/n",sizeof(struct arr),sizeof(int),sizeof(b[0]));

      

       return 0;

}

 

其中malloc分配了数据结构struct arr的大小再加上4个字节大小,实际上sizoef(struct arr) = 4 = sizoef(int),不包括零长度数组的大小,所以memset之后,buff的内容都是0x30,sizeof(b[0]) = 1。

 

2、case范围

GNU c支持case x…y这样的语法,区间[x, y]的数都满足这个case条件,如:

Switch(ch)

{

Case ‘0’ …’9’ : c -= ‘0’;

Break;

Case ‘a’…’f’ : c -= ‘a’ – 10;

Break;
}

 

3、语句表达式

GNU c把包含在括号的复合语句看做一个表达式,称为语句表达式,它可以出现在允许表达式出现的任何地方。我们可以在语句表达式中使用原本只能在复合语句中使用的循环变量、局部变量等。如:

 

#include <stdio.h>

 

#define min_t(type, x, y) /

({type __x = (x); type __y = (y); __x < __y ? __x : y;})

 

int main()

{

       int ia, ib, mini;

       float fa, fb, minf;

      

       ia = 1;

       ib = 2;

       fa = 1.1;

       fb = 2.2;

      

       mini = min_t(int, ++ia, ++ib);

       minf = min_t(float, ++fa, ++fb);

      

       printf("mini = %d/nminf = %f/n", mini, minf);

      

       return 0;

}

 

4、typeof关键字

Typeof(x)语句可以获得x的类型,因此,我们可以借助typeof重新定义min_t

#define min_t(x, y) ({      /

const typeof(x) __x = (x);  /

const typeof(y) __y = (y);  /

(void)(&__x == &__y);       /

__x < __y ? __x : __y;})

 

5、可变参数的宏

标准c只支持可变参数的函数,例如printf的原型为

int printf(const char *format {, argument}…);

在GNC c中,宏也可以接受可变数目的参数,如:

#define pr_debug(fmt, arg…) printfk(fmt, ##arg)

“##”是处理arg不代表任何参数的情况,这时候,前面的逗号就变得多余了,使用“##”之后,GNU c预处理器会丢弃前面的逗号

 

6、标号元素

指定数组的方法是在初始化值前添加“[index]=”,当然也可以用“[first … last] = ”的形式指定一个范围。如:

unsigned char data[MAX] = {[0 … MAX-1] = ‘a’};

 

7、当前函数名

GNU c预定义了两个标志保存当前函数的名字,__FUNCTION__保存函数在源码中的名字,__PRETTY_FUNCTION__保存带有语言特色的名字,在c函数中,这两个名字是相同的:

void example()

{

       printf("This is function: %s ", __FUNCTION__);

       printf("This is function: %s", __PRETTY_FUNCTION__);

}

输出:example example

 

8、特殊属性声明

GNU c允许函数、变量和类型的特殊属性,以便进行手工的代码优化和定制代码检查的方法。指定一个声明的属性,只需要在声明后添加__attribute__(( ATTRIBUTE ))。其中ATTRIBUTE为属性说明,如果存在多个属性,则以逗号分隔。GNU c支持noreturn、format、section、aligned、packed等十多个属性

 

noreturn属性作用于函数,表示该函数从不返回。这会让编译器优化代码,并消除不必要的警告信息。如:

#define ATTRIB_NORET __attribute__((noreturn))

asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET

 

format属性页用于函数,表示使用printf、scanf或strftime风格的参数,指定format属性可以让编译器根据格式串检查参数类型,如:

asmlinkage int printk(const char *fmt, …) __attribute__((format (printf, 1, 2)));

上述代码中的第一个参数时格式串,从第二个参数开始都会根据printf函数的格式串规则检查参数。

 

unused属性作用于函数和变量,表示该函数或变量可能不会被用到,这个属性可以避免编译器产生警告信息。

 

aligned属性用于变量、结构体或联合体,指定变量、结构体或联合体的对界方式,以字节为单位,如:

struct example_struct

{

       char a;

       int b;

       long c;

}__attribute__((aligned(4)));

表示该结构体的变量以4字节对界

 

packed属性作用于变量和类型,用于变量或结构体成员时表示使用最小可能的对界,用于枚举、结构体或联合体类型时,表示该类型使用最小的内存。如

struct example_struct

{

       char a;

       int b;

       long c __attribute__((packed));

};

编译器对结构体成语及变量对界的目的是为了更快的访问结构体或成员及变量占据的内存,如,对一个32位整形变量,若以4字节的方式存放(即低两位地址为00),则cpu在一个总线周期内就可以读取32为,若不然,cpu需要两次总线周期才能组合为32位整数

 

9、内建函数

GNU c提供了大量的内建函数,其中大部分是标准c库函数GNU c编译器内建版本,如memcpy等,它们与对应的标准c库函数功能相同。不属于库函数的其他内建函数的命名通常以__builtin开始。

 

do{}while(0)

在Linux内核中,do{}while(0);主要用于宏定义,可避免分支、逻辑、分号使用的错误,保证能无编译错误的使用宏,如

 

#include<stdio.h>

#include<malloc.h>

 

//#define SAFE_FREE(p) ({free(p); p = NULL;})

#define SAFE_FREE(p) do{free(p); p = NULL;}while(0)

 

int main()

{

       int *p = malloc(4);

      

       if(NULL != p)

              SAFE_FREE(p);

       else

              return -1;

 

       printf("success!/n");

 

       return 0;

}

 

goto

在Linux内核中队goto的应用非常广泛,但是一般只限于错误处理中,用于错误处理的goto用法简单而高效,只需保证在错误处理时注销、资源释放的顺序与正常的注册、释放申请的顺序相反。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值