文章目录
- 文件描述符及文件处理
- [open / create / close 函数](#open / create / close 函数)
-
- [open 函数](#open 函数)
- [create 函数](#create 函数)
- [close 函数](#close 函数)
- [read / write / lseek 函数](#read / write / lseek 函数)
-
- [read 函数](#read 函数)
- [write 函数](#write 函数)
- [lseek 函数](#lseek 函数)
- 标准I/O库
- 系统调用与库函数调用
文件描述符及文件处理
在 Linux 中设备和目录都可以看做文件(一切皆文件),主要包括四种类型:
① 普通文件 ,② 目录文件 ,③ 链接文件 ,④ 设备文件
文件描述符
Linux 的内核利用文件描述符来访问文件
-
定义
文件描述符是非负整数 ,作为索引指向内核中的进程打开文件表
当打开一个现存文件或新建一个文件时,内核会向进程返回一个文件描述符
当读写文件时,也需要使用文件描述符来指定待读写的文件
-
标准文件描述符
通常一个进程启动时,要打开三个文件:标准输入 、标准输出 和标准错误处理
它们的文件描述符分别为:0、1 和 2
在国际标准组织IEEE制定的ISO标准中,定义了宏符号常量
STDIN-FILENO
、STDOUT_FILENO
和STDERR_FILENO
,用于替代0、1 和 2(定义位于头文件<unistd.h>
中)0
:标准输入(STDIN_FILENO
)1
:标准输出(STDOUT_FILENO
)2
:标准错误(STDERR_FILENO
)
系统调用
系统调用是指操作系统提供给用户程序的一组"特殊"接口,用户程序可以通过这组"特殊"接口来获得操作系统内核提供的特殊服务
在Linux中用户程序不能直接访问内核 提供的服务
为了更好的保护内核空间,将程序的运行空间分为内核空间 和用户空间,他们运行在不同的级别上,在逻辑上是相互隔离的
【详细说明】

- 通常情况下,应用程序是通过**操作系统提供的应用编程接口(API)**而不是直接通过系统调用来编程
- 操作系统 API 通常都以 C 库函数的方式提供,Linux 也是如此
- 系统调用和 C 库函数之间并不是一一对应的关系,可能几个不同的函数会调用到同一个系统调用
- 系统命令位于C库的更上层,是利用 C 库实现的可执行程序,比如最为常用的 ls、cd 等命令
【系统调用的作用】
-
屏蔽硬件差异,提供统一接口(抽象底层设备)
系统调用可以为用户空间提供访问硬件资源的统一接口,应用程序不必去关注具体的硬件访问操作
(如读写文件时,应用程序不用去管磁盘类型,甚至于不用关心是哪种文件系统)
-
提高系统安全性,防止非法访问内核
系统调用可以对系统进行保护,保证系统的稳定和安全,系统调用的存在规定了用户进程进入内核的具体方式
(用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核,有了这样的进入内核的统一访问路径限制才能保证内核的安全)
文件处理
Linux对文件处理有两种方式:
-
系统调用 :直接与内核交互 ,如
open
、read
、write
特点:无缓冲,每次调用触发内核操作,效率较低但实时性强
【说明】
基于 Linux 的系统调用,由操作系统的系统调用完成对文件的操作
Linux 通过系统调用进行的文件处理,主要是指进行打开文件、读文件、写文件及关闭文件等 I/O 操作
大多数情况下,只需要用到5个函数:
open
、read
、write
、lseek
和close
这几个函数不需要经过缓冲就能立即执行,因此,被称为不带缓冲的I/O操作,即每一个函数都只调用内核中的一个系统调用
-
库函数调用 :封装系统调用,提供高级接口 ,如
fopen
,fread
,fwrite
特点:带缓冲,减少系统调用次数,提高效率(如全缓冲、行缓冲、无缓冲模式)
【说明】
基于 C 语言的库函数,标准 C 库函数提供的文件操作函数如
fopen
,fread
,fwrite
,fclose
,fflush
,fseek
等,它是独立于操作系统
【注意】系统调用是为了方便使用操作系统的接口,库函数则是为了人们程序的移植性更好
【无缓冲与有缓冲的区别】

- 无缓冲 I/O:立即执行操作,不借助用户态缓冲区
- 有缓冲 I/O:使用用户态缓冲区,I/O 操作可能延后执行(如
fwrite
需调用fflush
)
open / create / close 函数
open 函数
open
:打开或创建文件
调用 open
函数所需要的头文件如下:
c
#include <stdio.h>
#include <fcntl.h>
其函数为:
c
int open(const char *pathname, int oflag, int perms);
pathname
: 要打开或创建的文件的名字oflag
:可用来说明此函数的多个选择项O_RDONLY
:只读打开O_WRONLY
:只写打开O_RDWR
:读、写打开O_APPEND
:每次写时都加到文件的尾端O_CREAT
:若此文件不存在则创建它(使用此选择项时,需同时说明第三个参数,用其说明该新文件的存取许可权限)O_TRUNC
:如果此文件存在,而且为只读或只写成功打开,则将其长度截短为 0O_EXCL
:如果使用本参数,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件
perms
:权限(八进制 ,如0666
表示所有用户可读写),仅当创建新文件时才使用perms
参数
函数返回值:若文件打开成功则返回文件描述符 ;若出错则返回 -1
create 函数
creat
:以只写方式创建一个文件,若文件已经存在,则把它清空为 0
调用 creat
函数所需要的头文件如下:
c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
其函数为:
c
int creat(const char * pathname, mode_t mode);
//【注意】此函数等效于:
int open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
pathname
:要创建的文件名称mode
:跟 open 的第三个参数相同,可读,可写,可执行
函数返回值:若成功为只写打开的文件描述符 ,若出错为 -1
close 函数
close
:用于关闭一个打开的文件,并释放文件描述符
调用 close
函数所需要的头文件为:
c
#include <stdio.h>
其函数为:
c
int close (int fd);
fd
:文件描述符
函数返回值:若成功为0
,若出错为-1
当一个进程终止时,它所有的打开文件都由内核自动关闭,很多程序都使用这一功能而不显式地用 close 关闭打开的文件
【举例】用可读写方式新建(打开)一个文件
由于要以可读写的方式来打开文件,因此,对文件的操作权限为4(可读)及2(可写)之和,即操作权限为6;如果希望文件拥有者、同组用户、不同组用户均具有读和写的权限,则该文件的权限为666
c
#include<stdio.h>
#include<fcntl.h>
int main(){
int fd;
fd=open("a.txt",O_CREAT|O_TRUNC|O_RDWR,0600);
printf("open file: a.txt fd=%d \n",fd);
close(fd);
return 0;
}
read / write / lseek 函数
read 函数
read
:从打开的文件中读取数据
当 read 从终端设备文件中读取数据时,通常一次读取指定的字节数
调用 read
函数所需要的头文件为:
c
#include <unistd.h>
其函数为:
c
ssize_t read(int fd, void *buf, size_t count);
-
fd
:文件描述符,标识要读取的文件如果为 0,则从标准输入读数据,类似于
scanf()
的功能 -
buf
:指定存储器读取数据的缓冲区 -
count
:指定读取数据的字节数
函数返回值:实际读到的字节数,若返回值为0
,则已经达到文件尾,若返回-1
则出错
write 函数
write
:用于向打开的文件实现写入数据的操作
写操作的位置从文件的当前位移量处开始
若磁盘已满或超出该文件的长度,则 write 函数返回错误值
调用 write
函数所需要的头文件为:
c
#include <unistd.h>
其函数为:
c
ssize_t write(int fd, const void * buf, size_t count);
-
fd
:文件描述符,标识了要写入的目标文件例如:
fd 的值为1,就向标准输出写数据,也就是在显示屏上显示数据;
如果为 2 ,则向标准错误文件写数据
-
*buf
:待写入的字符缓冲区,是一个字符串指针 -
count
:要写入的字符数
函数返回值:为已写入的字节数,若返回 -1
则出错
对于普通文件,写操作从文件的当前位移量处开始
如果在打开该文件时,指定了O_APPEND
选择项,则在每次写操作之前,将文件位移量设置在文件的结尾处。在一次成功写之后,该文件位移量增加实际写的字节数
lseek 函数
lseek
:用于将文件指针定位到相应的位置,以进行读写操作
c
off_t lseek(int fd, off_t offset, int whence);
fd
:文件描述符offset
:偏移量,每一读写操作所需要移动的字节数,可正可负(向前、向后移)whence
:指出偏移的方式(有三种参数)SEEK_SET
:偏移到offset位置处(相对文件头)SEEK_CUR
:偏移到当前位置+offset位置处SEEK_END
:偏移到文件尾+offset位置处
函数返回值:调用成功则返回最终的偏移量 (从文件头开始数),调用失败则返回 -1
【举例1】返回当前的偏移量
c
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);
【举例2】返回文件大小
c
off_t currpos;
currpos = lseek(fd, 0, SEEK_END);
【举例1】创建一新文件 test.txt
,并把字符串"Hello, welcome to you"写到新创建的文件a.txt
中
c
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main(){
int fd;
fd=open("a.txt",O_CREAT|O_TRUNC|O_RDWR,0666);
char * buf="Hello,welcome to you.\n";
int len = strlen(buf);
int size = write(fd,buf,len);
printf("write to a.txt fd=%d char \n",size);
close(fd);
return 0;
}
【举例2】在当前目录,新建一个a.txt
文件,并编辑一些内容;通过程序,读取a.txt
中的内容
c
#include<stdio.h>
#include<fcntl.h>
int main(){
int fd;
char buf[50];
fd=open("a.txt",O_RDONLY);
printf("OPEN FILE : a.txt fd=%d \n",fd);
lseek(fd,0,SEEK_SET);
int size = read(fd,buf,22);
printf("READ FROM FILE: %s \n size=%d \n",buf,size);
close(fd);
return 0;
}
标准I/O库
-
fopen
函数:类似于系统调用中的open函数 -
fread/fwrite
函数:fread
函数从文件流中读取数据,对应于系统调用中的read
fwrite
函数从文件流中写数据,对应于系统调用中的write
-
fclose
函数:关闭指定的文件流 stream,使用fclose()
函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区 -
fflush
函数:用于强制将缓冲区中的内容写入文件
系统调用与库函数调用
