目录
- 0.预备知识
- 1.系统文件I/O
- 2.文件描述符fd
-
- [1.[0 & 1 & 2]](#1.[0 & 1 & 2])
- 2.什么是文件描述符?
- 3.文件描述符的分配规则
- 4.重定向
- [5.使用dup2系统调用 -- 完成重定向](#5.使用dup2系统调用 -- 完成重定向)
- 6.FILE
0.预备知识
- 什么叫做文件呢?
- 站在系统的角度,能够被input读取,或者能够output写出的设备就叫做文件
- 狭义文件:普通的磁盘文件
- 广义文件:显示器,键盘,网卡,声卡,显卡,磁盘,几乎所有的外设,都可以称之为文件
- 什么是当前路径 ?
- 当一个进程运行起来的时候,每个进程都会记录自己当前所处的工作路径
- C/C++默认会打开三个输入输出流,分别是stdin,stdout,stderr
- 如何给函数传递标志位?
- 用int中的不重复的一个bit位,来标识一种状态
cpp
#define ONE 0x1 //0000 0001
#define TWO 0x2 //0000 0010
#define THREE 0x4 //0000 0100
void show(int flags)
{
if(flags & ONE) printf("hello one\n");
if(flags & TWO) printf("hello two\n");
if(flags & THREE) printf("hello three\n");
}
int main()
{
show(ONE);
show(TWO);
show(ONE | TWO); //000 0001 | 0000 0010 = 0000 0011
show(ONE | TWO | THREE);
show(ONE | THREE);
return 0;
}
1.系统文件I/O
1.open
cpp
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
- open函数具体使用哪个,和具体应用场景相关
- 如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限
- 否则,使用两个参数的open
2.write/read/close/lseek
- 具体使用与open类似,直接man查询
- 功能类比C文件相关接口即可
- C语言库中的f#系列的函数,都是对系统调用的封装
2.文件描述符fd
1.[0 & 1 & 2]
- Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2
- 0,1,2对应的物理设备一般是:键盘,显示器,显示器
2.什么是文件描述符?
- 文件描述符就是从0开始的整数
- 当打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件
- 于是就有了file结构体,表示一个已经打开的文件对象
- 而进程执行open系统调用,所以必须让进程和文件关联起来
- 每个进程都有一个指针files ,指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针
- 本质上,文件描述符就是该数组的下标
- 只要拿到文件描述符,就可以找到对应的文件
3.文件描述符的分配规则
- 在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符
4.重定向
cpp
int main()
{
close(1);
int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
if (fd < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
exit(0);
}
- 本来应该输出到显示器上的内容,输出到了文件myfile当中
- fd=1 --> 这种现象叫做输出重定向
- 常见的重定向有:>,>>,<
- 重定向的本质:在OS内部,更改fd对应内容的指向
5.使用dup2系统调用 -- 完成重定向
- 把oldfd内容拷贝到newfd --> 最后要和谁一样? --> oldfd
- 这里拷贝的是file_struct中fd下标对应的file的地址
- 例子
- printf是C库当中的IO函数,一般往stdout中输出,但是stdout底层访问文件的时候,找的还是fd:1
- 但此时,fd:1下标所表示内容,已经变成了myfile的地址,不再是显示器文件的地址
- 所以,输出的任何消息都会往文件中写入,进而完成输出重定向
cpp
int main()
{
int fd = open("./log", O_CREAT | O_RDWR);
if (fd < 0)
{
perror("open");
return 1;
}
close(1);
dup2(fd, 1);
for (;;)
{
char buf[1024] = {0};
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0)
{
perror("read");
break;
}
printf("%s", buf);
fflush(stdout);
}
return 0;
}
6.FILE
- 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用
- 所以本质上,访问文件都是通过fd访问的 --> struct File中封装了fd