本文主要介绍一下用系统接口进行文件的访问:
首先来看代码:
hello.c写文件:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
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;
}
hello.c读文件:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd= open("myfile",O_RDONLY);
if(fd<0){
perror("open");
return 1;
}
const char *msg="hello world!\n";
char buf[1024];
while(1){
ssize_t s=read(fd,buf,strlen(msg));
if(s>0)
printf("%s\n",buf);
else
break;
}
close(fd);
return 0;
}
依次运行写文件和读文件,测试结果:
下面介绍下上述代码中用到的接口:open (man open)
参数介绍:
pathname:要打开或创建的目标文件
flags:打开文件时,可以传入多个参数选项,用下面的一个或多个常量进行“或”运算,构成flags:
- O_RDONLY:只读打开
- O_WDONLY:只写打开
- O_RDWR:读、写打开
以上三个常量必须指定一个,且只能指定一个
- O_CREAT:若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
- O_APPEND:追加写
mode_t理解:直接man手册,很清楚
open函数具体使用哪个,和具体应用场景有关,若目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,说到创建文件的默认权限,我们具体来看下:
- 新建文件夹默认权限=0666-权限掩码
- 新建目录默认权限=0777-权限掩码
- 超级用户默认掩码值为0022,普通用户默认掩码值为0002
说到这里,上述代码中open的各参数就很容易理解了。
然后就是write和read的参数了,也很简单:
write各参数:
fd:文件描述符(后面讲)
msg:缓冲区首地址(从哪开始写)
len:期望写入多少字节
read各参数:
fd:
buf:接收区首地址(往哪里读)
len:同上
学过C文件操作的,可以自己类比C文件相关接口。
下面介绍之前提到的文件描述符fd:
通过对open函数的学习,我们发现文件描述符实际上就是一个小整数
0&1&2
- Linux进程默认情况下会有3个缺省打开的文件描述符,分别是:标准输入0、标准输出1、标准错误2
- 0、1、2对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char buf[1024];
ssize_t s=read(0,buf,sizeof(buf));
if(s>0){
buf[s]=0;
write(1,buf,strlen(buf));
write(2,buf,strlen(buf));
}
return 0;
}
运行上面的程序,输入内容,回车,即可输出结果:
我们已经知道,文件描述符就是从0开始的小整数,当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体表示一个已经打开的文件对象,而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*file指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针。所以,本质上,文件描述符就是该数组的下标。因此,只要拿到文件描述符,就可以找到对应的文件。来张图感受一下:
文件描述符的分配规则
先来看代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
//close(0);
//close(2);
int fd=open("myfile1",O_WRONLY|O_CREAT,0644);
if(fd<0){
perror("open");
return 1;
}
printf("fd:%d\n",fd);
fflush(stdout);
close(fd);
exit(0);
}
运行结果:
关闭0或者2,再运行,发现结果是fd:0或fd:2,由此我们可以知道文件描述符的分配规则:在file_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
重定向
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
close(1);
int fd=open("myfile1",O_WRONLY|O_CREAT,0644);
if(fd<0){
perror("open");
return 1;
}
printf("fd:%d\n",fd);
fflush(stdout);
close(fd);
exit(0);
}
关闭1,再看结果,发现屏幕并未输出结果,而是将结果输出到了myfile1中:
上述这种现象叫做输出重定向,常见的重定向有:>,>>,<
直接看图体会:
printf是C库当中的IO函数,一般往stdout中输出,但是stdout底层访问文件的时候,找的还是fd:1,但此时fd:1下标所表示的内容已经变成了myfile1的地址,不再是显示器的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。