(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~https://blog.csdn.net/ky233?type=blog
点个关注不迷路⌯'▾'⌯
目录
一、文件操作符
1.什么是文件操作符
在我们调用系统接口的时候一般会这样写
int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);
我们在这里可以看到fd的返回值是int,说明是一个整数,其中如果程序错误就会返回-1,打开一个文件是从3开始的,那么0、1、2呢?
- 答案是Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2。
- 0,1,2对应的物理设备一般是:键盘,显示器,显示器
所以fd其实就是文件操作符!
2.FILE类型
我们知道一个进程是可以打开多个文件的,如果多个进程都打开自己的文件呢?那么要怎么管理呢?所以就需要先描述在组织了!
所以FILE是一个结构体,里面包含着一个被打开文件的几乎所有的内容!我们每打开一个文件就会创建一个这样的结构体,其中缓冲区也在这里面。
那么我们要怎么找呢?这就需要fd了!
文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数 组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件!
3.fd的分配规则
int main()
{
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
首先默认是fd是3,那么我们关掉0呢?
int main()
{
close(0);
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
发现是结果是:fd: 0,可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符。
二、重定向
1.重定向的原理
int main()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
//close(fd);
exit(0);
}
可是如果我们先关闭1呢?这个时候在把最后的close屏蔽,那么我们就会发现,打印的内容居然到了myfile里面!
这是因为1对应的是标准输出,但是我们把标准输出关掉了,那么,根据fd的规则,上面代码的fd就是1了,本来1是对应的标准输出,结果现在对应的是myfile里面!
这种现象叫做输出重定向!所以重定向的本质就是:在OS内部更改fd对应的内容指向!
2.使用dup2的系统调用
int dup2(int oldfd, int newfd);
这个函数的作用是把oldfd拷贝给newfd
所以是把3拷贝给1。
那么我们就可这样完成一个重定向
int mian(int argc,char* argv[])
9 {
10 if(argc != 2)
11 {
12 return 2;
13 }
14 int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);
15 if(fd<0)
16 {
17 perror("open");
18 return 1;
19 }
20 dup2(fd,1);
21 fprintf(stdout,"%s\n",argv[1]);
22 }
3.如何理解Linux下的一切皆文件
- 虽然底层不同的硬件,对应的是不同的操作方法
- 但是每一个设备的核心访问函数,都可以是read、write
- 所有的设备都有自己的read、write,但是代码的实现是不一样的
所以在OS看来一切皆是文件!
三、缓冲区
1.什么是缓冲区
缓冲区就是一个buffer数组,是为了提高我们整体的效率而存在
2.缓冲区的刷新策略
1.立刻刷新
2.行刷新
如printf的刷新位行刷新,遇到\n才会刷新,如磁盘文件
3.满刷新
缓冲区被写满的时候才会刷新,即便有\n也不会刷新
4.特殊情况
- 用户强制刷新fflush
- 进程退出
但是所有的设备都倾向于满缓冲因为这样可以减少IO的次数,可以减少访问外设的次数
3.用户级缓冲区
首先我们来看这段代码
int main()
{
fprintf(stdout, "hello world\n");
char* s = "hello Linux\n";
write(1, s, strlen(s));
fork();
return 0;
}
如果我们正常运行程序,只会打印hello world和hello Linux这两个字符串。
但是如果我们进行重定向那么就会发现hello world被打印了两份!!
这是因为,我们在显示器上打印是行刷新,当执行到fork的时候已经打印完成了,缓冲区内已经没有东西了,但是如果我们在磁盘上打印就会变成全缓冲,这个时候fork,缓冲区内还有东西,这个时候就发生了写时拷贝!在缓冲区内的数据就变成了父子进程各有一份,那么在进程结束后,父子进程就直接各自刷新了自己的缓冲区,而write则不进入用户级缓冲区,所以则打印一份!
我们可以在前面加上fflush就可以避免这样了