一、概述
u-boot的任务是启动内核,内核的任务是启动应用程序,应用程序会涉及很多文件和硬件操作,但不会直接操作硬件,由设备驱动来完成硬件的具体操作;比如读写文件,点灯、获取按键值。
Linux系统将设备分为3类:
-
字符设备:
指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后顺序。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。
-
块设备:
指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。
【每一个字符设备或块设备都在/dev目录下对应一个设备文件。linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备。】
-
网络设备:
网络设备可以是一个硬件设备,如网卡; 但也可以是一个纯粹的软件设备, 比如回环接口(lo).一个网络接口负责发送和接收数据报文。
二、应用程序与设备、驱动之间的关系
以led操作为例,进行描述。
应用程序中需要打开led灯,就需要open函数,在内核中的驱动程序中也有对应的led_open函数,这个led_open函数就是用来负责初始化led的引脚功能,应用程序中要调用read函数读取led灯的状态,内核中的驱动程序也有led_read函数。这是应用程序与内核中驱动程序一种最简单(不是唯一)的对应方式。
那么应用程序中的open、read函数最终怎样调用到驱动程序中的led_open、led_read呢,中间有哪些东西?
-
应用程序:
就是被调用的那些库函数,例如open、read、write... ... -
C库(系统调用):
其中的其实就是实现open、read这些函数来调用swi val 指令进入内核(函数不同val值都会不同) -
内核:
内核根据swi后面不同的值去调用VFS中的system_open/system_read/ system_write等异常处理函数 找到相应的驱动程序(VFS:virtual file system 虚拟文件系统)
int main()
{
int fd1 fd2;
int val=1;
fd1 = open(“/dev/led”,O_RDWR); //打开led
write(fd1, &val, 4);
fd2 = open(“hello.txt”,O_RDWR); //打开文本
write(fd2, &val, 4);
}
上面的应用程序主要实现点灯与打开文本文件,都是用的同样的函数。但是点灯与打开文本文件的行为显然不一样。那么谁来实现这些不一样的行为呢?
对于LED灯,有led_open驱动程序。对于文本文件存在于flash设备上,也有对于的驱动程序。system_open、system_read最终会根据打开的不同文件,找到底层的不同驱动程序,然后调用驱动程序中的硬件操作函数,比如led_open来实现对具体硬件设备的操作。
至于system_open又是如何找到led_open的,可参考:详解应用层open函数如何调用到底层驱动中xxx_open函数。