【嵌入式Linux】高级IO应用编程 学习笔记


Linux应用编程 和 网络编程 
嵌入式Linux核心课程 课程顺序:1裸机  2C高级  3uboot4linux应用编程 5驱动


典型的嵌入式产品开发的顺序
1、让Linux系统在硬件上跑起来(系统移植工作)
2、基于Linux系统来开发应用程序实现产品功能(应用编程属于这一步骤)


基于Linux做应用编程其实就是调用API来实现应用需要完成的一些任务。
注意:此课程是降低难度版
主要目的:使用linux内核提供的API和c库函数来实现一定的功能


层次:APP+OS 操作系统本身有一部分是驱动,下面是硬件
读写文件的案例中,文件是从硬盘中放着,当打开文件即将其读取到内存,这些硬件需要驱动。操作系统中有文件系统fs,
保证我们用目录+文件名的方式来访问他,给我们提供了一些访问接口,从而用这些“门”去操控硬件和文件。


------------------------------------------------------------------------------------------------
1.2 文件操作的主要接口API


app | OS | 硬件
操作系统对外开的门,就是API,其实质是一些函数。由OS提供,应用层使用


常用的文件IO接口
open 打开一个文件
close 关闭一个文件
lseek 移动文件指针
write 向文件中写入内容
read 从文件中读内容


Linxu文件操作的一般步骤:
1、先open一个文件,得到一个文件描述符 //失败就GG
2、对文件进行读写操作 
3、最后关闭文件 //不关可能导致损坏


文件平时是存在块设备中的文件系统中的,没有文件系统会直接去扇区中操作文件。
我们把存在块设备中没有打开的文件,称为静态文件


open一个文件时,Linux内核所做的:
1、内核在进程中建立了一个打开文件的数据结构,记录下来我们打开的文件。
2、申请一段内存,将静态文件从块设备读取到内存的一个特定地址存放,即动态文件
3、读写时内存中的动态文件就和块设备中的静态文件不同步了。close后更新块设备
//现象举例:不保存就关机/断电,文件内容会丢失。//这样设计的原因:块设备本身有读写限制。块设备的读写特点就是大片数据读写,而内存是可以很灵活的以字节读写、是可以随机访问的。

文件描述符:实质是一个数字。当我们open一个文件时,OS在内存中构建了一些数据结构来表示这个动态文件,返回一个数字作为文件描述符,以后我们应用程序要操作一个动态文件,就依靠文件描述符区分。
//简而言之:就是用爱区分一个程序打开的多个文件的。出了当前进程就会失去意义。
//A进程:打开文件3,B进程:打开文件3,这两个文件不同,是完全没有比较性的

---------------------------------------------------------------------------
1.3第一个程序

```
#include<stdio.h>
int main(int argv, char *argv[])
{
    //打开文件
    //读写文件 
    //关闭文件
}


    int fd = -1; //fd: file descriptor
    char buf[100]={0};//read()所使用的缓冲区
    int ret_read = 0;//read()的返回值

    //打开文件
    fd = open("a.txt",O_RDWR);//这些定义很难记住,man 2 open。返回值是新fd
    if(-1 == fd) //fd的合法范围>0 ,错误时返回-1
    printf(文件打开错误);
    else 成功 , 输出fd                  
    
    //读取文件
    ret = read (fd, buf, 20);

    //关闭文件
    close(fd); 
}
```

注意:
1、close函数的传参是文件描述符。
2、open返回的文件描述符fd千万不能丢,最后关闭也需要fd去指定关闭这个文件,如果在关闭之前丢掉了 fd,那么就没法关闭、没法读写了。
3、ssize_t read (fd ,buf ,size_t count)
ssize_t是内核用typedef重定义的类型,其实就是int。返回值是成功读取的字节数。为了可移植
fd 是读取的文件,一般由open获得
buf 是应用程序自己提供的一段缓冲区
count 是我们要读取的字节数
关于man的使用
man 1 xxx shell
man 2 xxx API
man 3 xxx 库函数

1.4 open函数flag详解
Linux中文件有读写权限,我们在open函数的时候也可以附带一定权限打开
读写权限相关:
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
打开已经存在并且有内容的文件时:
可能会有三种可能:  
1、新内容取代旧内容
2、新内容在原内容的前面 (很少有这种需求)
2、新内容在原内容的后面
4、不读不写的时候,原来的内容不变
O_ARREND 新内容接续到后面 【结果3】
O_TRUNC 不是空的,变成空的 【结果1】
不使用上述符号 【结果4】


退出进程:在打开/读写失败的时候,不应该继续让程序运行下去了
1、return -1; //只可以在main函数中使用
2、使用exit / _exit / _Exit //三个作用是一样的

exit(0); //使用的时候用一句代替return -1;

O_CREAT 有就清空,没有创建(有危险,怕弄错文件名)
O_EXCL | O_CREAT有就报错,没有创建 EXCL:不包括


O_CREAT单独使用的时候有文件被冲掉的威胁,但Windows不允许新文件与旧文件同名,会选择或覆盖
open函数在使用O_CREAT函数的时候,可以设定这个文件的权限(mode)。如0777
\
OS中阻塞与非阻塞的概念
阻塞式:当前我想干一件事,排队等候=.=
若一个函数是阻塞式的,调用这个函数时当前进程可能被卡住。(实质是函数内部条件不具备)
非阻塞式:嘿呀不能立马办,就先回去等一会儿再来
若一个函数是非阻塞式的,这个函数一定会立即返回,但不一定完成任务
操作系统提供的API和由API封装的库函数是区分阻塞和非阻塞式的,我们在调用的时候要非常清楚 
API会说的非常清楚,默认的是阻塞式的7,

O_NONBLOCK 以非阻塞式的方式打开一个文件。注意:只用于设备文件

write写一个东西,如果是阻塞式,而且还不能写,那么进程就不动了。
举个应用中的例子:12306打开的时候默认去连网,如果手机没联网,他拼命的尝试去连网。这种设计不好,应该先显示一个前台页面,先让我们看到,然后在后台去联网。

O_SYNC 阻塞式:保证底层完成后,才返回应用层
OS内核中有缓冲区,默认情况下APP到缓冲区就算结束了

这个时候硬件设备(如写入的硬盘)可能尚未更新

======================================================
1.6 文件读写 的 一些细节

errno 和 perror
【errno】
记录系统的最后一次错误代码,看是发生什么错误了。代码是一个int型的值


注意:只有当一个库函数失败时,errno才会被设置。当函数成功运行时,errno的值不会被修改。这意味着我们不能通过测试errno的值来判断是否有错误存在。反之,只有当被调用的函数提示有错误发生时检查errno的值才有意义。
查看错误代码errno是调试程序的一个重要方法。当linux C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因。在实际编程中用这一招解决了不少原本看来莫名其妙的问题。


  【perror】
perror("Open_error: .... ");
返回输出:Open_error: No such file or diretory!


文件IO和标准IO
文件io是API组成的一套体系。可以很好的完成读写,但效率不高 openclose
标准io是应用层c库函数提供的一些函数。是库函数不是API fopenfclose
标准io也是由文件io封装而来,内部也是用文件io组成的。加这个封装的目的主要是在应用层添加一个缓冲机制,在应用层构建了一个buffer,再通过fwrite就不再是写到内核buffer,而是直接写入应用层buffer,帮我们协调运送的buffer大小,也就是说,记录多次写入内容,等应用层buf大小足够大,然后一并送入内核,提高了效率。


文件io写入ABC(直接依次进内核)
写入A 内核buffer:A
写入B 内核buffer:AB
写入C 内核buffer:ABC
写入硬件设备


标准io写入ABC(适当打包进内核) 
写入A 应用层buffer:A内核buffer:NULL
写入B 应用层buffer:AB内核buffer:NULL
写入C 应用层buffer:ABC内核buffer:NULL
应用层buff写入: (A+B+C)    → 内核buff收到(ABC)


操作系统知道一次写入buf最好是几个,知道应用层的buffer应该设置多大,提高效率

======================================================
1.7 Linux管理文件

文件分类:1静态文件 2动态文件
硬盘区域分类:1硬盘内容管理表、2真正的存储内容区域
OS访问硬盘时,先去读取硬盘内容管理表,从中找到要访问的文件的 扇区级别的信息,利用这个信息查询真正存储内容的区域,得到需要的文件


操作系统如何通过文件名得到文件内容:
查询硬盘内容管理表。该表中含有一个文件的多种信息,封装在结构体中
这个结构体也叫信息列表,即inode。
里面大概就是文件名、所在硬盘扇区、块号等等


硬盘管理,是以文件为单位的。每个文件有独有的inode,对于一个结构体。


关于格式化:格式化分为两种,快速格式化和普通格式化。前者只是删除了硬盘内容管理表,所以非常快。普通格式化也就是底层格式化,是比较慢的。
快速格式化的内容是有可能被找回的,只要恢复i节点就可以了。
底层格式化了基本就没戏了,但是一些国家机关是有一些办法的。


内存中文件打开
在程序中打开的文件就属于某个进程。每个进程都有一个数据结构,用来记录进程的所有信息,叫做进程信息表。进程信息表中有一个指针 指向 文件管理表。
当前进程能找到文件,正是因为有这个机制。
文件管理表中用来索引打开的文件的index值就是文件描述符fd
最终找到的,就是一个已经被打开的 文件的管理结构体 vnode


文件与流的概念
stream :流
文件读写只允许 一个字符一个字符的进行,那么多个字符被挨个读写时,就构成了一个字符流
流的概念是动态的,他不是一个东西,而是一个运动状态
编程中提到流的概念,一般都是IO相关的,所以经常称之为 IO流。


【玛德喂,老朱说的麻烦死了】
上述过程,简而言之就是:表表相连。
inode 用来表示一个静态文件
vnode 用来表示一个动态文件v 

======================================================

1.8 lseek

光标:GUI模式下给人看的,标识当前正在操作的 位置
在动态文件中,会通过文件指针来表征正在操作的位置。lseek就是操作文件指针的
把光标移动的操作在内部就对应着lseek。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谦谦青岫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值