linux-文件IO

要看见树木,也要看见森林。

文章目录

  • 引言
  • 文件描述符
    • 作用
    • 从应用层写一个字符到文件,这中间发生了什么?
  • 打开文件
  • 关闭文件
  • 定位文件
  • 读文件
  • 写文件
  • 获取或设置文件属性

引言

文件IO,即与操作文件相关的输入输出函数。
POSIX标准定义了,linux文件IO函数指不带缓冲的IO函数,这些函数中的read和write将直接调用内核中的相应函数。
不带缓冲指数据直接由用户层到达内核层。

文件描述符

作用

程序要操作文件,必然需要在程序中用一种虚拟的方式来表示物理存在的文件。这种虚拟的方式就是文件描述符。
文件描述符可以理解为一个索引,通过这个索引能够获取一个文件的所有信息,例如偏移量,文件类型,访问权限等。

从应用层写一个字符到文件,这中间发生了什么?

打开一个文件,从上层到内核将涉及到3个结构体链表,分别是:文件描述符表、打开文件表和i-node表。它们之间的关系如下:
首先,文件是存储在文件系统中的,文件的属性、权限等信息,是文件系统在维护。一个文件拷贝到文件系统,文件系统将读取文件头部信息,并将文件的属性、权限等信息存储在i-node表中,然后写入磁盘设备中。
同时内核在运行时会维护一个打开文件表,当open文件时,内核将在打开文件表中创建一个结构体成员,存放文件信息,例如,当前文件偏移量、状态标识、文件放文件权限、文件属性等,而信息是从i-node表中读取到的。
open一个文件,将进入内核调用open相对应的系统函数,该系统函数在内核层为这个进程(调用open的进程)创建一个文件描述符表。文件描述符表包含有文件描述符和指向打开文件表的指针。
文件描述符:即在open时,内核给用户分配的一个非负整数。
打开文件表指针:指向内核的打开文件表中与该文件相关的结构体成员的地址。
可以理解文件描述符和打开文件表指针是绑定在一起的。应用层通过文件描述符和内核沟通,内核内部通过对应的打开文件表指针找到文件信息,并返回给应用层。
之后用户就可以通过该文件描述符,调用read/write,就找到打开文件表中与本文件相关的信息,例如文件内容等,然后内核调用相对应的系统调用函数,在read时,向上层返回查询到的信息,在write时,向文件系统磁盘上写入数据。

打开文件

open和openat
函数声明:
	#include 
	int open(const char *path, in oflag, .../* mode_t mode */)
	int openat(int fd, const char *path, in oflag, .../* mode_t mode */)
参数说明:
	path: 待打开或创建文件的名字(带路径)
	oflag: 
		下面这几个常量必须指定一个且只能指定一个:
		O_RDONLY: 只读打开
		O_WRONLY: 只写打开
		O_RDWR: 读写打开
		O_EXEC: 只执行打开,无法读写
	下面常量是可选的
		O_APPEND: 每次写文件追加到文件尾端
		O_CREAT: 若此文件不存在则创建它。若使用此选项,open函数需同时指定第3个参数,以指定访问权限位
		O_EXCL: 如果同时指定了O_CREAT,而文件已经存在,则出错。可用此参数判断一个文件是否存在,如果不存在,则创建。
		O_NONBLOCK: 设置后续对此此文件的io操作为阻塞方式。
		O_SYNC: 使每次write等待物理IO操作完成。
	mode:指定创建文件的访问权限,例0755 ,==》0:十进制;7:111,用户权限;5:101,读+可执行,组权限;5:其他用户权限
返回值:
	成功返回非负整型文件描述符,失败返回-1并设置errno。

例1:

	int ret = 0;
	//读写一个文件,如果文件不存在则创建
	ret = open("/path/file.txt", O_RDWR | O_CREAT, 0755);
	if(ret < 0)
	{
	perror("open");
	exit(-1);
	}
	//判断一个文件是否存在
	ret = open("/path/file.txt", O_RDWR | O_CREAT | O_EXCL, 0755);
	if(ret < 0)
	{
	printf("file exit!\n");
	}

关闭文件

当一个进程终止时,内核自动关闭它所有的打开文件。
函数声明:

include <unistd.h>  
int close(int fd);

定位文件

设置当前文件的偏移量。

函数声明:
 	#include 
	 off_t lseek(int fd, off_t offset, int whence); 
参数说明:
	 fd: 文件描述符
	 offset: 字节为单位,例如7,指偏移量为7。具体如果偏移,需要看whence。
	 whence: 指定偏移的起点
 		SEEK_SET: 文件的开始处
 		SEEK_CUR: 当前文件的偏移值
 		SEEK_END: 文件的末尾处
返回值:
	success: 返回新的文件偏移量
	fail:-1,在errno中存放错误代码

例1.

 //据文件开始处偏移10个字节
 off_t offset;
 offset = lseek(fd, 10, SEEK_SET);

读文件

函数声明:
	#include 
	ssize_t read(int fd, void *buf, size_t nbytes)
返回值:
	success: 非负数。返回读取到的字节数,如果到达文件尾端,则返回0。
	fail:-1

例1:

 ssize_t nBytes;
 nBytes = read(fd, buf, sizeof(buf));
 if(nBytes < 0)
 perror("read");

写文件

函数声明:
	#include 
	ssize_t write(int fd, const void *buf, size_t nbytes);
返回值:
	success: 返回写入的字节数,即nbytes
	fail: -1

获取或设置文件属性

设置/获取已经打开文件的属性。

函数声明:
 #include 
 int fcntl(int fd, int cmd, .../* int arg */);

例1、修改文件描述符标志位

 void setFl(int fd, int flags)
 {
  int val;
  if((val = fcntl(fd, F_GETFL, 0) < 0))
   printf("fcntl F_GETFL error\n");
  val |= flags;
  if(fcntl(fd, F_SETFL, val) < 0)
   printf("fcntl F_SETFL error\n");
 }

你可能感兴趣的:(linux环境编程,linux,编程语言)