Linux的文件IO使用

1. Linux 中 IO 的概念介绍

所有的 I/O 操作都是通过读文件或者写文件来完成的。在这里,把所有的外围设备,包括
键盘和显示器,都看成是文件系统中的文件

2. 什么是缓存 I/O

缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的
缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,
也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷
贝到应用程序的地址空间。缓存 I/O 有以下这些优点:
缓存 I/O 使用了操作系统内核缓冲区,在一定程度上分离了应用程序空间和实际的物理
设备。

缓存 I/O 可以减少读盘的次数,从而提高性能。
当应用程序尝试读取某块数据的时候,如果这块数据已经存放在了页缓存中,那么这块数
据就可以立即返回给应用程序,而不需要经过实际的物理读盘操作。当然,如果数据在应用程
序读取之前并未被存放在页缓存中,那么就需要先将数据从磁盘读到页缓存中去。对于写操作
来说,应用程序也会将数据先写到页缓存中去,数据是否被立即写到磁盘上去取决于应用程序
所采用的写操作机制:如果用户采用的是同步写机制( synchronous writes ), 那么数据会立
即被写回到磁盘上,应用程序会一直等到数据被写完为止;如果用户采用的是延迟写机制
( deferred writes ),那么应用程序就完全不需要等到数据全部被写回到磁盘,数据只要被写
到页缓存中去就可以了。在延迟写机制的情况下,操作系统会定期地将放在页缓存中的数据刷
到磁盘上。与异步写机制( asynchronous writes )不同的是,延迟写机制在数据完全写到磁
缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的
缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,
也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷
贝到应用程序的地址空间。缓存 I/O 有以下这些优点:
缓存 I/O 使用了操作系统内核缓冲区,在一定程度上分离了应用程序空间和实际的物理
设备。
缓存 I/O 可以减少读盘的次数,从而提高性能。

当应用程序尝试读取某块数据的时候,如果这块数据已经存放在了页缓存中,那么这块数
据就可以立即返回给应用程序,而不需要经过实际的物理读盘操作。当然,如果数据在应用程
序读取之前并未被存放在页缓存中,那么就需要先将数据从磁盘读到页缓存中去。对于写操作
来说,应用程序也会将数据先写到页缓存中去,数据是否被立即写到磁盘上去取决于应用程序
所采用的写操作机制:如果用户采用的是同步写机制( synchronous writes ), 那么数据会立
即被写回到磁盘上,应用程序会一直等到数据被写完为止;如果用户采用的是延迟写机制
( deferred writes ),那么应用程序就完全不需要等到数据全部被写回到磁盘,数据只要被写
到页缓存中去就可以了。在延迟写机制的情况下,操作系统会定期地将放在页缓存中的数据刷
到磁盘上。与异步写机制( asynchronous writes )不同的是,延迟写机制在数据完全写到磁盘
上的时候不会通知应用程序,而异步写机制在数据完全写到磁盘上的时候是会返回给应用程
序的。所以延迟写机制本身是存在数据丢失的风险的,而异步写机制则不会有这方面的担心

3 缓存 I/O 的缺点

在缓存 I/O 机制中,DMA 方式可以将数据直接从磁盘读到页缓存中,或者将数据从页缓
存直接写回到磁盘上,而不能直接在应用程序地址空间和磁盘之间进行数据传输,这样的话,
数据在传输过程中需要在应用程序地址空间和页缓存之间进行多次数据拷贝操作,这些数据拷
贝操作所带来的 CPU 以及内存开销是非常大的。
对于某些特殊的应用程序来说,避开操作系统内核缓冲区而直接在应用程序地址空间和磁
盘之间传输数据会比使用操作系统内核缓冲区获取更好的性能

在所有的 linux 系统中,如果需要对文件的进行操作,只要包含如下 4 个头文件即可。
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
上面四个头文件中包含了打开,关闭,创建,读文件,写文件的函数,还有标志位,以及
在不同 32 位以及 64 位系统下数据长度的宏变量定义。

1 open函数

使用 open 函数的时候会返回一个文件句柄,文件句柄是文件的唯一识别符 ID。对文件的
操作必须从读取句柄开始。
先来看一下函数 open 的两个原型。
int open(const char *path, int oflags);
int open(const char *path, int oflags,mode_t mode);
两个参数的 open 函数主要用于创建文件(和create、write、read函数一起使用)
open 函数可以建立一个到文件或者设备的访问路径。在打开或创建文件时可以制定文件
的属性及用户的权限等各种参数。
第一个参数 path 表示:路径名或者文件名。路径名为绝对路径名,例如开发板中的 led
驱动的设备几点/dev/leds。
第二个参数 oflags 表示:打开文件所采取的动作。

下面三个选项是必须选择其中之一的。
O_RDONLY 文件只读
O_WRONLY 文件只写
O_RDWR 文件可读可写
下面是可以任意选择的。
O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件
O_EXCL 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O
设置为非阻塞模式(nonblocking mode)
O_NDELAY 和 O_NONBLOCK 功能类似,调用 O_NDELAY 和使用的
O_NONBLOCK 功能是一样的。
第三个参数 mode 表示:设置创建文件的权限(一般为0777 可读可写)。
S_IRUSR,S_IWUSER,S_IXUSR,S_IRGRP,S_IWGRP,S_IXGRP,S_IROTH,S_IWOTH,S_IXOTH.
其中 R:读,W:写,X:执行,USR:文件所属的用户,GRP:文件所属的组,OTH:其
他用户。第三个参数可以直接使用参数代替。

下面是简单的open.c

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void main()
{
 int fd;
 char *leds="/dev/leds";    //节点存在
 char *test1="/bin/test1";  //节点不存在
 char *test2="/bin/test2";  //节点不存在

 if((fd=open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0)   //打印正确
   printf("open %s failed!\n",leds);
 printf("\n%s fd is %d \n",leds,fd);

 if((fd=open(test1,O_RDWR,0777))<0)    //打印错误
  printf("open %s failed!\n",test1);
 printf("%s fd is %d\n",test1,fd);
 
 if((fd=open(test2,O_RDWR|O_CREAT,0777))<0)  //打印正确 表明创建成功
   printf("open %s failed!\n",test2);
 printf("%s fd is %d\n",test2,fd);
}

在linux 下使用交叉编译 arm-none-linux-gnueabi-gcc -o open open.c -static

2.creat函数

关于 creat 函数,首先这个单词并不是表示创建的意思,创建的英文单词是“create”,
这是早期的一个小的拼写错误,却一直沿用下来。
在介绍 open 函数的时候,可以看到 open 函数有两种形式,一个是两个参数一个是三个
参数,早期的时候 open 只有三个参数的形式,三个参数的形式会导致 open 函数无法打开一
个未创建的文件,也就是无法建立文件,所以就有了这个 creat 函数。
现在 creat 函数可以完全用 open 替代,考虑到在阅读代码的时候可能会碰到,所以简单
介绍一下。
creat 函数原型如下。
int creat(const char * pathname, mode_t mode);
creat 函数只有两个参数,参数的含义和 open 类似。
大家看到这个函数的时候知道它是创建文件的就成,在写代码的时候完全可以用 open 代
替。

3.close函数

任何一个文件在操作完成之后都需要关闭,这个时候需要调用 close 函数。
调用 close 函数之后,会取消 open 函数建立的映射关系,句柄将不再有效,占用的空间
将被系统释放。
close 函数在头文件“#include <unistd.h>”中,close 函数的使用和参数都比较简单.
int close(int fd);
参数 fd,使用 open 函数打开文件之后返回的句柄。
返回值,一般很少使用 close 的返回值。

4.write函数

对文件进行写操作,write 函数使用的比较多
write 函数在头文件“#include <unistd.h>”中。
函数原型为 ssize_t write(int fd,const void buf,size_t count)
参数 fd,使用 open 函数打开文件之后返回的句柄。
参数
buf,需要写入的数据。
参数 count,将参数*buf 中最多 count 个字节写入文件中。
返回值为 ssize 类型,出错会返回-1,其它数值表示实际写入的字节数。

write.c

#include<stdio.h>

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>



void main()
{
  int fd;
  char *testwriter="/bin/testwrite";
  ssize_t length_w;
  char buffer_writer[]="Hello Write Function!";

  if((fd=open(testwriter,O_RDWR|O_CREAT,0777))<0)
    printf("open %s failed\n",testwriter);
  
  //将Buffer 写入
  length_w=write(fd,buffer_writer,strlen(buffer_writer));
  if(length_w==-1)
   perror("write");
  else
   printf("Write Function OK!\n");

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

运行之后将会看到 “Write Function OK!”。

5.read函数

对文件进行写操作,read 函数使用的比较多。
read 函数在头文件“#include <unistd.h>”中。
函数原型为 ssize_t read(int fd,void buf,size_t len)
参数 fd,使用 open 函数打开文件之后返回的句柄。
参数
buf,读出的数据保存的位置。
参数 len,每次最多读 len 个字节。
返回值为 ssize 类型,出错会返回-1,其它数值表示实际写入的字节数,返回值大于 0 小
于 len 的数值都是正常的。

read.c

#include<stdio.h>

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

#define MAX_SIZE 1000



void main()
{
 int fd;
 ssize_t length_w,length_r=MAX_SIZE,ret;
 char *testwrite="/bin/testwrite";
 char buffer_write[]="Hello Read Function!";
 char buffer_read[MAX_SIZE];

 if((fd=open(testwrite,O_RDWR|O_CREAT,0777))<0) //使用 open 函数打开或者新建"/bin/testwrite"文件
  printf("open %s is failed!",testwrite);
 
 length_w=write(fd,buffer_write,strlen(buffer_write)); //使用 write 函数将 buffer 中的内容写到"/bin/testwrite"文件中。
 if(length_w==-1)
  perror("write");
 else
  printf("Write Function OK!\n");
 
 close(fd);

  if((fd=open(testwrite,O_RDWR|O_CREAT,0777))<0)
   printf("open %s is failed!",testwrite);
  if(ret=read(fd,buffer_read,strlen(buffer_write))) //使用 read 函数,将"/bin/testwrite"文件中的内容读出来
   perror("read");
 printf("file content is %s \n",buffer_read); //使用打印函数 printf 打印 read 函数读出的数据
 close(fd); //调用 close 函数关闭打开的文件,程序结束

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值