一、文件数据输出/输入。
1、如何读取文件的数据? -> read() -> man 2 read
函数功能:read from a file descriptor
//读取一个文件描述符的数据
头文件:
#include <unistd.h>
原型:
ssize_t read(int fd, void *buf, size_t count);
参数:
fd:文件描述符
buf:数据缓冲区
count:尝试读取的字节数(愿望值)
返回值:
成功:已经成功读取到的字节数
失败:-1
例子1: 尝试从文本中读取一些数据出来。
注意:从文本文档中读取出来的数据,都是字符串。
int main(int argc,char *argv[])
{
//1. 打开目标文件
int fd;
fd = open("./test.txt",O_RDONLY);
if(fd < 0)
{
printf("open file error!\n");
}
//2. 读取数据
char buf[100] = {0};
int n;
n = read(fd,buf,5);
printf("n = %d\n",n);
printf("from file:%s\n",buf);
//3. 关闭文件
close(fd);
return 0;
}
结果:
n = 10
from file:helloworld
练习1: 重复读取一个文件,那么第二次读取的时候是在第一次基
础上继续读,还是重新读? -> 继续读
int main(int argc,char *argv[])
{
//1. 打开目标文件
int fd;
fd = open("./test.txt",O_RDONLY);
if(fd < 0)
{
printf("open file error!\n");
}
//2. 读取数据
char buf[100] = {0};
int n;
n = read(fd,buf,5);
printf("n = %d\n",n);
printf("from file:%s\n",buf); //hello
n = read(fd,buf,5);
printf("n = %d\n",n);
printf("from file:%s\n",buf); //world
//3. 关闭文件
close(fd);
return 0;
}
2、写入数据到文件中。 -> write() -> man 2 write
函数功能: write to a file descriptor
//写入数据到文件描述符中
头文件:
#include <unistd.h>
原型:
ssize_t write(int fd, const void *buf, size_t
count);
参数:
fd:文件描述符
buf:需要写入到文件中的内容
count:写入的字节数
返回值:
成功:真正写入字节数
失败:-1
例子2:尝试写一些数据到空白的文件中。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
fd = open("./test2.txt",O_WRONLY|O_TRUNC);
if(fd < 0)
{
printf("open file error!\n");
}
char buf[20] = "helloworld";
int n;
//n = write(fd,buf,strlen(buf)); //内容是:helloworld
//printf("n = %d\n",n); //10
//n = write(fd,buf,5); //内容是:hello
//printf("n = %d\n",n); //5
//n = write(fd,buf,20); //内容是:helloworld + 10个空
格
//printf("n = %d\n",n); //20
n = write(fd,buf,100); //内容是:helloworld + 10个空
格 + 乱码
printf("n = %d\n",n); //n=100
close(fd);
return 0;
}
练习2: 重复写入一个文件,那么第二次写入的时候是在第一次基
础上继续写,还是重新写? -> 继续写
n = write(fd,buf,5); //内容是:hello
printf("n = %d\n",n); //5
n = write(fd,buf,5); //内容是:hello
printf("n = %d\n",n); //5
文件内容:hellohello
练习3: 先读取一些数据,然后再写,那么写的时候是重新写,还
是读取完的那个位置继续写? -> 继续写
文本内容:yueqianhelloworld
read(fd,buf,5); //yueqi
printf("buf = %s\n",buf); //yueqi
char *p = "apple";
write(fd,p,strlen(p)); //内容:yueqiappleloworld
练习4: 验证O_APPEND在写操作时,会追加? -> 会追加。
打开一个文件,写入数据 -> 从头开始写的。
打开一个文件|O_APPEND,写入数据 -> 从文件末尾开始写。
fd = open("./test2.txt",O_RDWR|O_APPEND); //
yueqiappleloworld
char *p = "kkk";
write(fd,p,strlen(p));
文本内容:yueqiappleloworldkkk
O_APPEND会影响读操作吗? -> 不会
打开一个文件,读取数据 -> 从头开始读
打开一个文件|O_APPEND,读取数据 -> 从头开始读
二、文件偏移量。
1、什么是文件偏移量?
文件偏移量就是文件当前的定位,默认打开一个文件时,文件的定位都
是在最开头。
2、怎么样才能使得文件偏移量发生偏移?
1)使用读写操作的函数可以使得文件偏移量发生偏移。
fd=open("test.txt"); //偏移量:0
write(fd,"hello",5); //偏移量:5
2)如何使得不调用读写函数前提下发生偏移? -> lseek() -> man
2 lseek
函数功能:reposition read/write file offset
//重新定位读写的偏移量
头文件:
#include <sys/types.h>
#include <unistd.h>
原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
fd:需要发生偏移的文件的文件描述符
offset:需要偏移的字节数 [+] (往后偏移) [-] (往前偏
移)
whence:
SEEK_SET: 相对于文件的开头。
SEEK_CUR: 相对于当前的位置进行偏移。
SEEK_END: 相对于文件的末尾发生偏移。
返回值:
成功:距离文件开头字节数。
失败:-1
例子:验证lseek的参数。
int main(int argc,char *argv[])
{
int fd;
fd = open("./test.txt",O_RDWR); //偏移量:0
helloworld
int ret;
ret = lseek(fd,3,SEEK_SET); //偏移量:3
printf("ret = %d\n",ret);//3
char buf[10] = {0};
read(fd,buf,5);
printf("from file:%s\n",buf);//lowor
ret = lseek(fd,-3,SEEK_CUR);
printf("ret = %d\n",ret);//5
read(fd,buf,5);
printf("from file:%s\n",buf);//world
close(fd);
return 0;
}
如果当前偏移量已经在开头,还往前偏移会怎么样?
lseek()函数就会返回失败,返回-1,当前的文件定位还在0。
如果当前偏移量已经在文件的末尾,还往后偏移会怎么样?
会发生偏移,但是如果偏移完之后没有写入东西,那么在windows中看
到还是在文件末尾。
如果发生偏移之后,写入东西了,那么就会在windows中看到空格。
三、linux系统IO应用实例。 -> LCD液晶屏幕。
1、在linux下,一切都是文件。
连lcd液晶屏幕也是一个文件,既然是一个文件,那么lcd液晶对应的文
件名是什么?
lcd液晶 -> 硬件设备 -> 去开发板下"/dev"目录下寻找。
/dev/fb0 -> lcd液晶设备
/dev/input/event0 -> 触摸屏设备
/dev/ttySAC0 -> 拓展串口1
/dev/ttySAC1 -> 拓展串口2
/dev/ttySAC2 -> 拓展串口3
/dev/ttySAC3 -> 拓展串口4
2、已知lcd液晶设备名字,就可以使用open函数去访问设备,如果我们
要写一些数据(颜色)进去,那么首先必须要了解lcd液晶参数。
1)屏幕尺寸:7寸
2)分辨率:800*480 -> 像素点总数
3)每一个像素点都是由三原色组成的,所以像素点可以显示任何一种颜
色。 三原色:红绿蓝。
那么每一个像素点占用多少个字节?
[root@GEC6818 /]#cat /sys/class/graphics/fb0/bits_per_pixel
32 -> 每一个像素点 = 32位
-> 每一个像素点 = 4个字节,分别是ARGB
例子: 尝试写颜色到lcd屏幕上,看看有没有效果?
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
/* 1. 访问lcd液晶屏幕 */
int lcd;
lcd = open("/dev/fb0",O_WRONLY);
if(lcd < 0)
{
printf("open lcd error!\n");
}
/* 2. 准备颜色,然后写入到lcd设备上 */
int color = 0x00FF0000; //红色
write(lcd,&color,4);
/* 3. 关闭文件 */
close(lcd);
return 0;
}
结果:
整个屏幕的第一个像素点显示为红色。
练习1: 显示满屏的紫色。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
/* 1. 访问lcd液晶屏幕 */
int lcd;
lcd = open("/dev/fb0",O_WRONLY);
if(lcd < 0)
{
printf("open lcd error!\n");
}
/* 2. 准备颜色,然后写入到lcd设备上 */
int color = 0x00FF00FF; //紫色
int i;
for(i=0;i<800*480;i++)
{
write(lcd,&color,4);
}
/* 3. 关闭文件 */
close(lcd);
return 0;
}
练习2: 验证0x00FFFF00是黄色。 -> 是的
练习3: 黑色和白色是多少?
黑色:0x00000000
白色:0x00FFFFFF
四、内存映射。
1、内存映射方式与普通文件IO方式有什么区别?
例如:想把一些数据写入到文件中。
普通文件IO:
open()访问文件 -> 得到一个文件描述符fd -> 直接往文件描述符fd
写入数据就可以了 -> 关闭文件描述符fd。
内存映射:
open()访问文件 -> 得到一个文件描述符fd -> 根据文件描述符fd去
内存空间上映射一块空间,得到一个地址p -> 用户只需要将数据拷贝
到内存空间上就可以了 -> 对应的文件就会有相应的变化 -> 撤销
映射 -> 关闭文件描述符fd。
2、内存映射主要作用对象:lcd设备。
详细步骤:
1)通过访问文件的方式,得到文件描述符。
int lcd = open("/dev/fb0",O_RDWR);
2)根据文件描述符lcd去内存空间上映射一块空间。 -> mmap() ->
man 2 mmap
函数功能:map files or devices into memory
//将文件/设备映射到内存空间上
头文件:#include <sys/mman.h>
原型:
void *mmap(void *addr, size_t length, int prot, int
flags,
int fd, off_t offset);
参数:
addr:不为NULL -> 用户选择内存空间上的地址。
0.0000001%
NULL -> 系统为用户选择空间。 99.99999%
length:映射的内存长度 例如:lcd液晶 800*480*4
prot:
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
如果需要使用多个权限,则使用"|"连接在一起,例
如: PROT_READ|PROT_WRITE
flags:
MAP_SHARED -> 共享的
MAP_PRIVATE -> 私有的
fd:文件描述符
offset:文件偏移量,(从文件的那个字节开始偏移)
返回值:
成功:指向那片内存空间的区域的地址
失败:-1
3)拷贝数据到空间上。 -> memcpy() -> man 3 memcpy
函数的功能: copy memory area
//拷贝数据到内存空间上
头文件:
#include <string.h>
原型:
void *memcpy(void *dest, const void *src, size_t n);
参数:
dest:目标内存空间的地址。
src:需要拷贝的数据(颜色)
n:需要拷贝的总字节数。
返回值:
成功:指向dest这个区域的地址
失败:NULL
4)那么对应的文件就会有对应的效果。
5)撤销映射。 -> munmap() -> man 2 munmap
功能: unmap files or devices into memory
//撤销映射
头文件:
#include <sys/mman.h>
原型:
int munmap(void *addr, size_t length);
参数:
addr:需要撤销内存空间的地址。
length:需要撤销的长度。
返回值:
成功:0
失败:-1
6)关闭文件。
close(lcd);
例题2:使用内存映射的方式来显示全屏紫色。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
int main(int argc,char *argv[])
{
int lcd,i;
int *p = NULL;
/* 1. 访问lcd液晶设备 */
lcd = open("/dev/fb0",O_RDWR);
if(lcd < 0)
printf("open lcd error!\n");
/* 2. 根据文件描述符fd去内存上映射一块区域 */
p = (int*)mmap(NULL,800*480*4,PROT_READ|
PROT_WRITE,MAP_SHARED,lcd,0);
if(p == (void *)-1)
printf("mmap error!\n");
/* 3. 准备颜色 */
int red_color = 0x00FF00FF;
/* 4. 将颜色数据拷贝到映射的空间上 */
for(i=0;i<800*480;i++)
{
memcpy(p+i,&red_color,4);
}
/* 5. 撤销映射 */
munmap(p,800*480*4);
/* 6. 关闭文件 */
close(lcd);
return 0;
}
练习4: 将int*,修改为char*,程序应该怎么改?
练习5: 使用内存映射完成以下的效果。