我们在C语言中学习了很多文件相关的接口,如fopen,fclose,fwrite,fread等等。并且,C会默认打开三个输入输出流u,分别是stdin,stdout,stderr。这三个流的类型都是*FILE,文件指针。接下来我们看一下文件相关的系统调用接口,看看它和C语言的文件接口有什么关系。
文件相关系统调用接口
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
/*
pathname:要打开或创建的目标文件
flags:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读,写打开
(这三个常量,必须指定一个且只能指定一个)
O_CREAT:若文件不存在,则创建它,需要使用mode选项,来指明新文件的访问权限
O_APPEND:追加写
返回值:
成功:新打开的文件描述符
关闭:-1
*/
close
#include <unistd.h>
int close(int fd);
/*
返回值:
0表示关闭成功,-1表示关闭失败
*/
read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
/*
fd:文件描述符
buf:读取的内容装到这里
count:读取文件的大小
返回值:
实际读取的字节数,0,表示读到文件结尾,-1,出错,
实际读取的字节个数,并不一定等于count
*/
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
/*
buf:要想写出去的数据在哪里
count:数据的个数
返回值:返回实际写出去多少个字节
*/
C语言文件接口和文件系统调用接口的关系
我们看一个C文件接口写的代码:
#include <stdio.h>
#include <string.h>
int main( void )
{
FILE *fp = fopen("myfile", "w");
if(!fp)
{
printf("fopen error!\n");
}
const char *msg = "hello world\n";
int count = 5;
while(count--)
{
fwrite(msg, strlen(msg), 1, fp);
}
fclose(fp);
return 0;
}
再来看一个用系统调用接口写的代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
umask(0);
int fd = open("myfile", O_WRONLY|O_CREAT, 0644);
if(fd < 0)
{
perror("open");
return 1;
}
int count = 5;
const char *msg = "hello world!\n";
int len = strlen(msg);
while(count--)
{
write(fd, msg, len);
}
close(fd);
return 0;
}
运行之后,发现这两个函数的功能是一模一样的,这说明什么问题呢?
要搞清楚这个问题之前那,我们先来认识两个概念:系统调用和库函数
- 库函数:fopen、fclose、fread、fwrite都是C标准库当中的函数,我们称之为库函数(libc)
- 系统调用接口:open、close、write、lseek(重新定位读/写文件偏移量)都属于系统提供的接口,我们称之为系统调用接口。
再来看一张图:
系统调用和库函数的关系,一目了然。所以,f#系列的函数,都是对系统调用的封装,方便二次开发。