1.open函数
1.1第二个参数的解释;
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
上面三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写
open("test.txt",O_WRONLY|O_CREAT,0644/*八进制给初始值*/);
1.2open的返回值(重点)
返回值是struct file*指针数组的下标
0:标准输入1:标准输出2:标准错误
#include<iostream>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
int main()
{
int fd1=open("test.txt",O_WRONLY|O_CREAT,0644/*°???????*/);
int fd2=open("test.txt1",O_WRONLY|O_CREAT,0644/*°???????*/);
int fd3=open("test.txt2",O_WRONLY|O_CREAT,0644/*°???????*/);
int fd4=open("test.txt3",O_WRONLY|O_CREAT,0644/*°???????*/);
printf("%d %d %d %d\n",fd1,fd2,fd3,fd4);
return 0;
}
执行结果:
原理:
2.文件描述符fd
2.1文件描述符的分配规则
上面说了,进程PCB中有一个指向files_struct的指针,这个结构体中又包含一个struct file的指针数组,打开文件就会在指针数组依次添加
那么关闭默认打开的标准输入、标准输出、标准错误文件,会发生什么结果呢?
关闭标准输入,那么新打开文件的文件描述符就是0了
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
int main()
{
close(0);
int fd1=open("test.txt",O_WRONLY|O_CREAT,0644);
if(fd1<0)
{
cerr<<"open fail";
return 1;
}
cout<<"fd1:"<<fd1<<endl;
return 0;
}
有上述可以得出文件描述符的分配规则:在files_struct指针数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符。
2.2输入输出重定向
把标准输出关闭在打开一个新文件,再打印就会发现不会打印在显示器,创建的文件中有我们打印的内容,出现了一个输出重定向;
#include<iostream>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
int main()
{
close(1);
int fd1=open("test.txt",O_WRONLY|O_CREAT,0644);
if(fd1<0)
{
cerr<<"open fail";
return 1;
}
cout<<"fd1:"<<fd1<<endl;
return 0;
}
输出重定向原理:
追加重定向只是在打开文件时加一个O_APPEND
#include<iostream>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
int main()
{
close(1);
int fd1=open("test.txt",O_WRONLY|O_CREAT|O_APPEND/*这里修改了*/,0644);
if(fd1<0)
{
cerr<<"open fail";
return 1;
}
cout<<"fd1:"<<fd1<<endl;
return 0;
}
我前面执行了几次
输入重定向
#include<iostream>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
int main()
{
close(0);
int fd=open("./test.txt",O_RDONLY);
if(fd<0)
{
cerr<<"open fail";
return(1);
}
char s[50];
while(fgets(s,sizeof(s)-1,stdin))
cout<<s;
return 0;
}
2.3dup2
需要重定向是就关闭文件是不是有些麻烦了,使用dup2函数也可以做到一样的效果;
原理:将oldfd拷贝newfd(标准输入、标准输出、标准错误)
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
int fd=open("test.txt",O_WRONLY|O_TRUNC|O_CREAT);
if(fd<0)
{
cerr<<"opne fail";
return 1;
}
dup2(fd,1);
cout<<"hello wrold"<<endl;
cout<<"hello IO"<<endl;
return 0;
}
2.4提出一些问题
Q:如果程序替换文件描述符会改变吗?
A:不会,因为指向文件结构体的指针保存在PCB中的指向的files_struct结构体中的指针数组内,文件描述符是这个指针数组的下标,程序替换替换的是代码和数据;
Q:创建子进程,子进程文件描述符会怎么初始化和文件会新增吗?
A:子进程的PCB是使用父进程的PCB来初始化的,子进程创建PCB,文件描述符使用父进程初始化所以相同,文件当然不会新增,文件在磁盘中只有一份;
3.缓冲区
进程退出时,会将FILE内部的数据刷新到系统缓冲区,再调用系统接口,用户->OS
三种刷新策略:
1.不缓存(直接刷新)
2.行缓冲\n,endl,例:输出到显示器
3.全缓冲(把缓冲区填满就刷新到内核缓冲区),例:写入磁盘文件
#include<stdio.h>
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
const char* s1="hello buffer\n";
write(1,s1,strlen(s1));
printf("hello world\n");
fprintf(stdout,"hello world\n");
close(1);
return 0;
}
执行结果:原本打印显示器重定向写到磁盘文件,刷新策略由行刷新变为了全刷新,在函数结束前关闭了磁盘文件,保存在FILE的字符串生命周期结束,write是系统调用接口所以在写入的磁盘文件中;
3.1缓冲区属于PCB、代码、数据哪一个?
#include<stdio.h>
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
const char* s1="hello buffer\n";
write(1,s1,strlen(s1));
printf("hello world\n");
fprintf(stdout,"hello world\n");
fork();
return 0;
}
执行结果:缓冲区的数据属于数据,所以遵守写实拷贝的规则,父子进程谁先刷新谁就写实拷贝