目录
1.回顾C文件接口
stdin、stdout、stderr
2.系统文件I/O
3.接口介绍
4.open函数返回值
5.文件描述符fd
5.1 0&1&2
1.回顾C文件接口
hello.c写文件
#include<stdio.h>
#include<string.h>
int main()
{
FILE *fp = fopen("myfile","w");
if(!fp){
printf("fopen error!\n");
}
const char *msg = "hello friend!\n";
int count = 5;
while(count--)
{
fwrite(msg,strlen(msg),1,fp);
}
fclose(fp);
return 0;
}
hello.c读文件
#include<stdio.h>
#include<string.h>
int main()
{
FILE *fp = fopen("myfile","r");
if(!fp){
printf("fopen error!\n");
}
char buf[1024];
const char *msg = "hello friend!\n";
while(1)
{
//
size_t s = fread(buf,strlen(msg),1,fp);
if(s>0){
buf[s] = 0;
printf("%s",buf);
}
if(feof(fp)){//用于检测文件是否读到末尾
break;
}
}
fclose(fp);
return 0;
}
输出信息到显示器
#include<stdio.h>
#include<string.h>
int main()
{
const char *msg = "hello fwrite\n";
fwrite(msg,strlen(msg),1,stdout);
printf("hello printf\n");
fprintf(stdout,"hello fprintf\n");
return 0;
}
stdin、stdout、stderr
stdin(0):键盘文件
stdout(1):显示器文件
stderr(2):显示器文件。(错误信息输出)
进程会默认打开键盘、显示器三个流的类型都是FILE* ,fopen返回值类型,文件指针
2.系统文件I/O
操作文件,除了上述C接口,我们还可以采用系统接口来进行文件访问。(文件其实是在磁盘上的,磁盘是外部设备,访问磁盘文件其实是访问硬件。几乎所有的库只要是访问硬件设备,必定要封装系统调用,因为之前讲到过,操作系统并不相信用户,不会与用户直接接触,所以提供了接口给用户)
先来直接以代码的形式,实现上面一模一样的代码:
hello.c写文件
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.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 friend\n";
int len = strlen(msg);
while(count--)
{
write(fd,msg,len);//fd:后面讲, msg:缓冲区首地址, len:本次读取,期望写入多少个字节的数据,
//返回值:实际写了多少字节数据
}
close(fd);
return 0;
}
hello.c读文件
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
int fd = open("myfile",O_RDONLY);
if(fd<0){
perror("open");
return 1;
}
const char *msg = "hello friend\n";
char buf[1024];
while(1){
size_t s = read(fd,buf,strlen(msg));
if(s>0){
printf("%s",buf);
}else{
break;
}
}
close(fd);
return 0;
}
3.接口介绍
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
4.open函数返回值
在认识返回值之前,下来认识两个概念:
系统调用:系统调用是操作系统提供给用户程序的一种接口,用于让用户程序请求操作系统内核提供的服务。当用户程序需要执行一些特权操作(如文件操作、进程管理等),无法直接访问硬件资源,需要通过系统调用向操作系统内核发出请求。系统调用是通过中断或异常机制实现的,可以看作是用户程序与操作系统内核之间的桥梁。
- 库函数:库函数是一组提供特定功能的函数,被封装在软件库中以供程序开发者使用。库函数通常是由编程语言或第三方开发者编写的,用于简化开发过程、提高代码的可重用性和可维护性。库函数可以包含在标准库或第三方库中,程序可以通过链接库函数的方式来调用其中的功能
- 上面的fopen fclose fread fwrite都是C标准库当中的函数,我们称之为库函数(libc)。
- 而,open close read write lseek都属于系统提供的接口,称之为系统调用接口
系统调用接口和库函数的关系一目了然
所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发
5.文件描述符fd
文件描述符就是一个整数
5.1 0&1&2
- Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2.
- 0,1,2对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.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系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files,指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针。所以,本质上,文件描述符就是数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。