早期在写C语言接口的时候,我们可以通过fopen来打开一个文件,下面这段两段代码为例:
hello.c写文件
1 #include <stdio.h>
2 #include <string.h>
3 int main ()
4 {
5 FILE* fp=fopen("myfile","w");
6 if(!fp){
7 printf("fopen error!\n");
8 }
9 const char *msg="hello world!\n";
10 int count = 5;
11 while(count--){
12 fwrite(msg,strlen(msg),1,fp);
13 }
14 fclose(fp);
15 return 0;
16 }
hello.c读文件
1 #include <stdio.h>
2 #include <string.h>
3 int main ()
4 {
5 FILE* fp=fopen("myfile","r");
6 if(!fp){
7 printf("fopen error!\n");
8 }
9 char buf[1024];
10 const char *msg="hello world!\n";
11 while(1){
12 ssize_t s=fread(buf,1,strlen(msg),fp);
13 if(s>0){
14 buf[s]=0;
15 printf("%s",buf);
16 }
17 if(feof(fp)){
18 break;
19 }
20 }
21 fclose(fp);
22 return 0;
23 }
显而易见,是要往“myfile”文件中写五条“helloworld”,再通过读文件接口读出来。这里我们运用了fwrite、fread这些C语言接口。
如果我们想把一个字符串显示在显示器上,我们可以用 printf、putchar、fputchar、fputs、fprintf、fwrite.......等等一系列接口。而我们也知道任何一个进程在启动之后,默认打开三个输入输出流,分别为stdin、stdout、stderr,分别对应的设备是键盘、显示器、显示器。仔细观察发现,这三个流的类型都是FILE*,我们用fopen打开文件该返回值的类型就是FILE*。而这个FILE*是C语言提供的数据类型,所以说,这三个流与C语言强相关,都是C语言提供的接口。
这是我们的操作系统结构,通过这张图以及之前的知识我们知道,操作系统不会直接将自己的每个管理模块暴露出来给用户,而是通过一系列的系统调用接口。而刚刚提到的C语言提供的那些接口都是处于用户操作接口的lib中。
但是刚才的代码表明我们最终将消息打印到了显示器上,即硬件。可是C库操作接口在上层的用户操作接口处,而硬件是在下面的硬件部分,所以说明我们必定要将要写入的数据自顶向下的交付给底层的硬件,但是要交付肯定不是C库直接去交付,因为我们没有这个权利,这就必定要通过操作系统来完成,也就是要通过系统调用接口。也就是说,我们的C库中,必定封装了对应的系统调用接口。
所以,我们现在可以以open为例看看系统调用接口,并且用系统调用接口来实现刚才的代码。
open
#include <sys/sypes.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
类比C库调用接口,我们明白fopen底层必定调用了open。 下面用文件的系统调用接口来实现刚刚的代码
hello.c写文件
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7
8 int main ()
9 {
10 umask(0);//设置允许当前进程创建文件或者目录最大可操作的权限
11 int fd = open("myfile",O_WRONLY|O_CREAT,0644);
12 if(fd<0) {
13 perror("open");
14 return 1;
15 }
16 int count = 5;
17 const char *msg = "hello world!\n";
18 int len = strlen(msg);
19 while (count--){
20 write(fd,msg,len);//fd:文件描述符 msg:缓冲区首地址 len:本次读取期
望写入多少字节的数据 返回值:实际写了多少字节的数据
21 }
22 close(fd);
23 return 0;
24 }
hello.c读文件
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7
8 int main ()
9 {
10 int fd = open("myfile",O_RDONLY);
11 if(fd<0) {
12 perror("open");
13 return 1;
14 }
15 const char *msg = "hello world!\n";
16 char buf[1024];
17 while(1){
18 ssize_t s=read(fd,buf,strlen(msg));//类比write
19 if(s>0){
20 printf("%s",buf);
21 }else{
22 break;
23 }
24 }
25 close(fd);
26 return 0;
27 }
open、close、read、write、lseek 都属于系统提供的接口,称之为系统调用接口。所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。