Linux基础IO(三)之访问文件的本质
文章目录
- 访问文件的本质
- 文件描述符fd
- 0 & 1 & 2
- 文件描述符的分配规则
访问文件的本质
文件描述符fd
fd
文件描述符就是一个小整数int,本质是数组的下标
FILE
c库自己封装的结构体。这里面必定封装文件描述符fd
。
验证:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd1=open("log1.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
int fd2=open("log2.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
int fd3=open("log3.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
int fd4=open("log4.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
if(fd1<0)
{
printf("open fail!\n");
return 1;
}
printf("fd1:%d\n",fd1);
printf("fd2:%d\n",fd2);
printf("fd3:%d\n",fd3);
printf("fd4:%d\n",fd4);
return 0;
}
0 & 1 & 2
Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
0,1,2对应的物理设备一般是:键盘,显示器,显示器
证明:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
const char*msg="hello linux!\n";
write(1,msg,strlen(msg));
write(2,msg,strlen(msg));
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char buffer[1024];
//现在读出来的是字节,不是字符串
ssize_t s=read(0,buffer,sizeof(buffer));
if(s<0)return 1;
buffer[s]='\0';
//c语言要把读出来的信息当成字符串,所以要加\0
printf("echo : %s\n",buffer);
return 0;
}
所以,进程默认就给我们打开了0(标准输入),1(标准输出),2(标准错误).
这个特性是操作系统的特性。
进程默认会打开键盘、显示器、显示器。
OS为什么默认要打开?
因为编程要用键盘输入,和显示器查看结果。
所以输入输出还可以采用如下方式:
而现在知道,文件描述符就是从0开始的小整数。
当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。
于是就有了file结构体,表示一个已经打开的文件对象。
而进程执行open系统调用,所以必须让进程和文件关联起来。
每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,
每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。
所以,只要拿着文件描述符,就可以找到对应的文件.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
printf("stdin->fd:%d\n",stdin->_fileno);
printf("stdout->fd:%d\n",stdout->_fileno);
printf("stderr->fd:%d\n",stderr->_fileno);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(1);
int n=printf("stdin->fd:%d\n",stdin->_fileno);
printf("stdout->fd:%d\n",stdout->_fileno);
printf("stderr->fd:%d\n",stderr->_fileno);
fprintf(stderr,"ret : %d\n",n);
return 0;
}
在stdin中,向显示器 printf
打印了12个字符,但是因为关掉了1(显示器),
所以,我们看不到打印的 stdin->fd:0
那为什么 fprintf
可以打印出来呢?
因为stderr文件描述符是2号,关闭的是1号
一个文件可以被多个进程同时打开
count引用计数
一个指针指向一个文件,count++
关闭文件:count–,将文件描述符的指针置空。
count==0 释放 struct file
对象
文件描述符的分配规则
open成功就return fd
,否则就return -1,
因为fd本质是数组下标,所以没有负数,负数就是失败。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define filename "log.txt"
int main()
{
int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
if(fd<0)
{
perror("open fail!\n");
return 1;
}
printf("fd:%d\n",fd);
const char*msg="Hello Linux!\n";
int cnt=3;
while(cnt--)
{
write(fd,msg,strlen(msg));
}
close(fd);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define filename "log.txt"
int main()
{
close(0);
int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
if(fd<0)
{
perror("open fail!\n");
return 1;
}
printf("fd:%d\n",fd);
const char*msg="Hello Linux!\n";
int cnt=3;
while(cnt--)
{
write(fd,msg,strlen(msg));
}
close(fd);
return 0;
}
close(0); -> close(1);
为什么把1号文件描述符关了就什么都不输出了?
printf
是要向显示器打印的,但是显示器文件描述符stdout
(fd=1
)关闭了,所以显示不出来。
进程所匹配的文件描述符已经被关掉了。
close(1); -> close(2);
又可以显示了,因为 printf
是向1号文件描述符打的,现在关闭2号,所以可以显示。
文件描述符的分配规则:
在 files_struct
数组当中,从0下标开始,找到当前没有被使用的最小的一个下标,作为新的文件描述符。