Linux基础IO
本节重点:
- 站在操作系统层面理解文件
- 学习文件系统(学习磁盘,学习Linux经典文件系统结构)
- 学习重定向,缓冲区学习
- 动静态库学习,学习制作动静态库
文件操作:我们使用语言层面的
fopen,fwrite......
都是对系统调用的封装,实质上是OS系统调用!!!PS:OS是硬件的管理者
系统调用和库函数
fopen fclose fread fwrite
都是C标准库当中的函数,我们称之为库函数,而 open close read write lseek
都属于系统提供的接口,称之为系统调用接口
1.umask:
Linux 中umask
的工作方式与chmod
命令类似,它也用于定义文件或目录的权限。它们之间的区别在于chmod
用于改变已有文件或目录的权限,而umask
用于定义新建文件或目录的默认权限。
权限数值对照表
0 | — | 无权限 |
1 | –x | 只具有执行权限 |
2 | -w- | 只具有写权限 |
3 | -wx | 具有写和执行全新啊 |
4 | r– | 只具有读权限 |
5 | r-x | 具有读和执行权限 |
6 | rw- | 具有读和写权限 |
7 | rwx | 具有读、写和执行权限 |
2.系统调用文件接口:
open close read write stat
类比C文件相关接口。
fopen fclose fread fwrite
都是C标准库当中的函数对系统接口进行封装,而 open close read write stat
都属于系统提供的接口,称之为系统调用接口
向文件写入实质上是操作系统向硬件写入
//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: 追加写
mode_t mode:文件权限码
返回值:
成功:新打开的文件描述符
失败:-1
//close()
include <unistd.h>
int close(int fd);
//write()
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
//read()
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
3.深入了解具体原理
如何对文件进行管理
文件是由进程打开,而一个进程是可以打开多个文件。系统中也存在着大量的进程那么也就意味着系统中任何时刻都可能存在大量的进程。而我们打开一个文件,需要将文件的相关属性加载到内存当中,操作系统是做管理工作的软件。那么OS系统需要对应这些数据进行管理如何进行管理?先描述在组织。操作系统会给每个打开的文件创建一个结构体struct_file.并以双项链表的方式将其组织起来。OS的打开文件的管理也就变成了对链表的增删查改等操作。
那么进程怎么知道,哪些文件是我打开的了?为了区分文件是那个进程打开的,还需要建立进程和文件之间的对应关系。我们在学习进程的时候,当我们的程序跑起来会将对应的代码和数据加载到内存,并为之创建相关的数据结构(task_struct ,mm_struct,页表)。并通过页表建立虚拟地址和物理地址之间的映射关系。
文件描述符fd
task_struct 有一个指针指向了一下结构体,这个结构体叫做files_struct,结构体里面有一个数组fd_array,而这个数组的下标就是我们说的fd.当进程打开log.txt文件时,我们需要先将该文件从磁盘当中加载到内存,形成对应的struct file,将该struct file连入文件双链表,并将该结构体的首地址填入到fd_array数组当中下标为3的位置,使得fd_array数组中下标为3的指针指向该struct file,最后返回该文件的文件描述符给调用进程即可。
文件描述符分配规则:
查找自己的文件描述表,在files_struct存储的数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
0 & 1 & 2
所以我们只要通过文件描述符就可以获取打开文件的相关信息并对其进行一系列操作。我们之前验证了文件描述符默认是从3开始的,也就是说0,1,2是默认被打开的。0代表的是标准输入流,对应硬件设备为键盘;1代表标准输出流,对应硬件设备是显示器;2代表标准错误流,对应硬件设备为显示器。当一个进程被创建时,OS就会根据键盘、显示器、显示器形成各自的struct file,将这3个struct file链接到文件的双链表当中,并将这3个struct file的地址分别填入fd_array数组下标为0、1、2的位置,至此就默认打开了标准输入流、标准输出流和标准错误流。
Linux下一切皆文件!!!
底层不同的硬件,一定对应不同的操作方法,但是OS管理这些底层硬件,使用统一的结构来进行管理的,在OS视角,底层硬件没有任何区别。
4.重定向
看现象:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
close(1);
int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);
if (fd < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
printf("fd: %d\n", fd);
fprintf(stdout,"hello world\n");
fflush(stdout);//fflush()在C语言级别上将语言级别的文件缓冲区刷新到内核文件缓冲区中
close(fd);
return 0;
}
此时我们发现,本来应该输出到显示器上的内容,输出到了文件"test.txt"中,其中,fd=1
。这种现象叫做输出重定向。常见的重定向有>, >>, <
关于上述现象解释:
fflush()
在C语言级别上将语言级别的文件缓冲区刷新到内核文件缓冲区中,当我们提前关闭1号显示器后,内核默认给新的文件分配标识符为1,两个库函数本质是向1号标识符写入内容,因此写入了test.txt文件中
自己实验下看看注释
fflush(stdout)
或者close(fd)
现象又是如何呢?
重定向原理:
在内核中改变文件描述符表特定下标的内容
fflush()
是在C语言级别上将语言级别的文件缓冲区刷新到内核文件缓冲区中
dup2系统调用:
SYNOPSIS
#include <unistd.h>
int dup2(int oldfd, int newfd);
DESCRIPTION
These system calls create a copy of the file descriptor oldfd.
//dup2()将oldfd 拷贝给newfd,它两都指向同一个文件,实现底层的重定向
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
const char *filename = "log.txt";
int main()
{
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
int fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
int fd = open("/dev/pts/2", O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (fd < 0)
{
perror("open");
return 1;
}
dup2(fd, 1);
printf("hello world\n");
fprintf(stdout, "hello world\n");
return 0;
}
缓冲区:
- 用户级缓冲区
- 内核级缓冲区
用户级别缓冲区刷新策略:
- 立即刷新:接口自主调用
- 行刷新:显示器
- 全缓冲,满了才自动刷新:普通文件
- 进程退出,系统自动刷新(强制)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
const char *filename = "log.txt";
int main()
{
// C
printf("hello printf\n");
fprintf(stdout, "hello fprintf\n");
// system call
const char *msg = "hello write\n";
write(1, msg, strlen(msg));
//fork();
return 0;
}
- 重定向到一个txt文件内容顺序有何区别呢?
- 是否fork()会有什么区别呢?
5.Linux文件系统
LinuxEXT2文件系统:
[ 根目录 ]
|
|-- [目录项] -- [索引节点(inode)]
| |
| |-- 文件属性信息
| |-- 数据块指针
|
|-- [子目录]
| |
| |-- [目录项] -- [索引节点(inode)]
| |
| |-- 文件属性信息
| |-- 数据块指针
|
|-- [文件]
|
|-- [目录项] -- [索引节点(inode)]
|
|-- 文件属性信息
|-- 数据块指针