嵌入式Linux入门学习教程汇总:嵌入式Linux教程—裸机、应用、驱动完整教程目录
在Linux系统中,一切都是“文件”:普通文件、驱动程序、网络通信等等。所有的操作,都是通过“文件IO”来操作的。
IO就是input和output,文件IO就是文件的读写。文件没有打开时是存放在块设备中的文件系统里的,这样的文件叫做静态文件。
操作一个文件一般是先打开(open)一个文件,得到这个文件的文件描述符,然后对文件进行读写(read/write)或其他操作,最后关闭(close)文件。
当我们open一个文件的时候,内核在进程中建立一个打开文件的数据结构来记录我们打开的文件,然后在内存中申请一段内存,将静态文件的内容读取到内存中特定地址管理存放,此时内存中的就是动态文件。
之后的读写等操作都是针对这份内存中的动态文件,当close关闭动态文件后,内核将内存中动态文件的内容同步更新到静态文件中。
文件描述符的概念:一个程序打开一个文件就会得到一个文件描述符(整数),该数字用来区分一个程序打开的多个文件。文件描述符的作用域就是当前进程。
普通文件类型
Linux中最多的一种文件类型, 包括 纯文本文件(ASCII);二进制文件(binary);数据格式的文件(data);各种压缩文件.第一个属性为 [-]
目录文件
就是目录, 能用cd 命令进入的。第一个属性为 [d],例如 [drwxrwxrwx]
块设备文件
块设备文件 : 就是存储数据以供系统存取的接口设备,简单而言就是硬盘。例如一号硬盘的代码是 /dev/hda1等文件。第一个属性为 [b]
字符设备
字符设备文件:即串行端口的接口设备,例如键盘、鼠标等等。第一个属性为 [c]
套接字文件
这类文件通常用在网络数据连接。可以启动一个程序来监听客户端的要求,客户端就可以通过套接字来进行数据通信。第一个属性为 [s],最常在 /var/run目录中看到这种文件类型
管道文件
FIFO也是一种特殊的文件类型,它主要的目的是,解决多个程序同时存取一个文件所造成的错误。FIFO是first-in-first-out(先进先出)的缩写。第一个属性为 [p]
链接文件
类似Windows下面的快捷方式。第一个属性为 [l],例如 [lrwxrwxrwx]
Linux中文件扩展名
windows里通过扩展名来区分文件类型的。linux里文件扩展名和文件类型没有关系。但为了容易区分和兼容用户使用windows的习惯,还是会用扩展名来表示文件类型。举例如下:
● 源码.tar、.tar.gz、.tgz、.zip、.tar.bz表示压缩文件,创建命令一般为tar,gzip,zip等。
● .sh表示shell脚本文件,通过shell语言开发的程序。
● .pl表示perl语言文件,通过perl语言开发的程序。
● .py表示python语言文件,通过python语言开发的程序。
● .html、.htm、.php、.jsp、.do表示网页语言的文件。
● .conf表示系统服务的配置文件。
● .rpm表示rpm安装包文件。
#include
#include
#include
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
返回值:open函数的返回值如果操作成功,它将返回一个文件描述符,如果操作失败,它将返回-1。
pathname:在open函数中第一个参数pathname是指向想要打开的文件路径名,或者文件名。我们需要注意的是,这个路径名是绝对路径名。文件名则是在当前路径下的。
flags:flags参数表示打开文件所采用的操作,我们需要注意的是:必须指定以下三个常量的一种,且只允许指定一个
以下的常量是选用的,这些选项是用来和上面的必选项进行按位或起来作为flags参数。
以下三个常量同样是选用的,它们用于同步输入输出
mode:表示设置文件访问权限的初始值,和用户掩码umask有关,比如0644表示-rw-r–r–,也可以用S_IRUSR、S_IWUSR等宏定义按位或起来表示,详见open(2)的Man Page。
文件权限由open的mode参数和当前进程的umask掩码共同决定。
第三个参数是在第二个参数中有O_CREAT时才作用,如果没有,则第三个参数可以忽略
mode 的 具体 参数:
S_IRWXU
00700 允许 文件 的 属主 读 , 写 和 执行 文件
S_IRUSR (S_IREAD)
00400 允许 文件 的 属主 读 文件
S_IWUSR (S_IWRITE)
00200 允许 文件 的 属主 写 文件
S_IXUSR (S_IEXEC)
00100 允许 文件 的 属主 执行 文件
S_IRWXG
00070 允许 文件 所在 的 分组 读 , 写 和 执行 文件
S_IRGRP
00040 允许 文件 所在 的 分组 读 文件
S_IWGRP
00020 允许 文件 所在 的 分组 写 文件
S_IXGRP
00010 允许 文件 所在 的 分组 执行 文件
S_IRWXO
00007 允许 其他 用户 读 , 写 和 执行 文件
S_IROTH
00004 允许 其他 用户 读 文件
S_IWOTH
00002 允许 其他 用户 写 文件
S_IXOTH
00001 允许 其他 用户 执行 文件
#include
ssize_t read(int fd, void *buf, size_t count);
返回值:读到文件末尾返回0;正常则返回读到的字节数;当有错误发生时则返回-1,错误代码存入errno中。
fd:文件描述符,buf:读取数据存放位置,count:需要读取的数据大小
#include
ssize_t write(int fd, const void *buf, size_t count);
返回值:如果顺利write()会返回实际写入的字节数(len)。当有错误发生时则返回-1,错误代码存入errno中。
fd:文件描述符,buf:待写入数据,count:待写入数据大小
#include
int close(int fd);
#include
#include
off_t lseek(int fd, off_t offset, int whence);
(1)文件指针:当我们要对一个文件进行读写时,一定需要先打开文件,所以我们读写的所有文件都是动态文件。动态文件在内存中的形态就是文件流的形式。
(2)文件流很长,里面有很多个字节。那我们当前正在操作的是哪个位置?GUI模式下的软件用光标来识别这个当前正在操作的位置,这个给人看的
(3)在动态文件中,通过文件指针来表征这个正在操作的位置。所谓文件指针,就是我们文件管理表这个结构体里面的一个指针。所以文件指针其实是vnode中的一个元素。这个指针表示当前我们正在操作文件流的那个位置。这个指针不能被直接访问,Linux系统用lseek函数来访问这个文件指针。
(4)当我们打开一个空文件时,默认情况下文件指针指向文件流的开始。所以这个时候去write时写入就是从文件开头开始的。write和read函数本身自带移动文件指针的功能,所以当我write了n个字节后,文件指针会自动向后移动n位。如果需要人为的随意更改文件指针,那就只能通过lseek函数了
(5)read和write函数都是从当前文件指针处开始操作的,所以当我们用lseek显示的将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的。
返回值:成功返回当前位置到开始的长度,失败返回-1并设置errno
fd:文件描述符,offset:偏移量
whence:位置
SEEK_SET:The offset is set to offset bytes. offset为0时表示文件开始位置。
SEEK_CUR:The offset is set to its current location plus offset bytes. offset为0时表示当前位置。
SEEK_END:The offset is set to the size of the file plus offset bytes. offset为0时表示结尾位置
lseek返回的是偏移量,所以如下其实就是返回文件长度
ret = lseek(fd,0,SEEK_END);
#include
int ioctl(int fd, unsigned long request, ...);
函数说明:
fd:表示文件描述符;
request:表示与驱动程序交互的命令, 用不同的命令控制驱动程序输出我们
需要的数据;
… :表示可变参数 arg, 根据 request 命令, 设备驱动程序返回输出的数据。
返回值: 打开成功返回文件描述符,失败将返回-1。
ioctl 的作用非常强大、灵活。不同的驱动程序内部会实现不同的 ioctl,APP 可以使用各种 ioctl 跟驱动程序交互:可以传数据给驱动程序,也可以从驱动程序中读出数据。
这里不作深入讲解,只需知道基本内容:
Linux应用程序调用的open()、read()等函数时,触发内部(32位cpu:swi,64位cpu:svc)指令,触发异常(在触发异常时还会传入不同的参数给内核,根据参数来分辨),然后调用内核中对应的sys_open()、sys_read()等函数。
会分辨目标文件到底是七种文件中的哪一种,例如普通文件,就去调用文件系统对应的驱动代码,读取块设备上的文件。例如目标文件是我们想驱动的字符设备文件,就会去调用们自己写的字符设备驱动程序中的read(),write()等。
挺简单的,直接上代码了
#include
#include
#include
#include
int main(int argc,char **argv)
{
int src;
int dst;
int buf[1024];
int read_len,write_len;
if(argc!=3)
{
printf("Input Error\n");
return -1;
}
src=open(argv[1],O_RDONLY);
if(src<=0)
{
printf("open %s error\n",argv[1]);
return -1;
}
dst=open(argv[2],O_RDWR|O_CREAT,S_IRWXU);
if(dst<=0)
{
printf("open %s error\n",argv[2]);
return -1;
}
while(1)
{
read_len=read(src,buf,1024);
if(read_len<0)
{
printf("read %s error\n",argv[1]);
return -1;
}
if(read_len>0)
{
write_len=write(dst,buf,read_len);
if(write_len<0)
{
printf("read %s error\n",argv[1]);
return -1;
}
}
else
break;
}
close(src);
close(dst);
return 0;
}
把源代码编译
gcc -o copy copy.c
使用vi命令随便写点东西
vi test1
使用我们自己的copy命令
./copy test1 test2
成功。