Linux - 基础IO

本文回顾了C语言文件操作接口,介绍系统文件I/O的open、close等接口。阐述文件描述符概念,包括其与进程、文件的关系及重定向原理。还讲解了文件系统,涉及硬盘类型、文件元数据、inode等,最后分析软硬链接区别,并对比fopen和open函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.回顾c语言的接口

1.向文件中写入数据(hello world)

#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
//FILE --->它是c语言中的一个文件指针(结构体)
	FILE * fp = fopen("log.txt","w");
	if(!fp)
	{
		perror("fopen");
		return 1;
	}
	const char* msg = "hello world\n";
	int i = 0;
	for(; i < 10 ; ++i)
	{
		fwrite(msg,1,strlen(msg),fp);
		//第一个参数表示写入字符串的首地址,第二个参数是写入的基本单位是多少,第三个参数表示写入多少个
		//第四个参数是给哪个文件写入。
	}
	fclose(fp);
}

2.向显示器上打消息的方法
1)任何进程启动时,会默认打开三个输入输出流,stdin,stdout,stderr;
三种的io流的返回值都是FILE*
2)stdin :标准输入------》键盘
stdout:标准输出-------》显示器
stderr:标准错误--------》显示器
3)Linux下,一切皆文件。

#include<stdio.h>
int main()
{
	const char * msg = "hello world\n";
	fputs(msg,stdout);
	printf("%s\n",msg);
	fprintf(stdout , "%s\n" ,msg);
}

3.打开文件的方式
“r”:只能从文件中读数据,该文件必须先存在,否则打开失败
“w”:只能向文件写数据,若指定的文件不存在则创建它,如果存在则先删除它再重建一个新文件
“a”:向文件增加新数据(不删除原有数据),若文件不存在则打开失败,打开时位置指针移到文件末尾
“r+”:可读/写数据,该文件必须先存在,否则打开失败
“w+”:可读/写数据,用该模式打开新建一个文件,先向该文件写数据,然后可读取该文件中的数据
“a+”:可读/写数据,原来的文件不被删去,位置指针移到文件末尾
fseek:操控文件的读写位置。
ftell:告诉当前写入的位置在哪里。
rewind:回到当前写入的最开始时。

二.系统文件I/O

1.操作文件的四个接口open,close,write,read,
open

int open(const char * pathname,int flags);
//第一种通常打开已经存在的文件,flags就是告诉你打开文件想要干什么(读写);
int open(const char * pathname,int flags, mode_t mode);
//第二种如果文件不存在,则继续创建,mode告诉文件的权限是什么。
pathname: 要打开或创建的目标文件 flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。 

参数:       
O_RDONLY: 只读打开       
O_WRONLY: 只写打开        
O_RDWR  : 读,写打开                  
这三个常量,必须指定一个且只能指定一个        
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限       
O_APPEND: 追加写 

返回值:        
成功:新打开的文件描述符        
失败:-1 

write

ssize_t write(int fd,const void * buf ,size_t count);
//fd写入那个文件描述符,buf表示写入的缓冲区,写多少个字符。

read

ssize_t read(int fd,void * buf,size_t count)
//fd读入的文件描述符,buf表示读到哪里,提供的缓冲区,第三个表示读多少个数据。

pathname:要打开或者要创建的目标文件。
flags:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量来进行“ | ” 运算,构成flags。

参数:
		O_RDONLY:只读打开。
		O_WRONLY:只写打开。
		O_RDWR:读,写打开。
				这三个常量,必须指定一个,且只能指定一个。
		O_CREAT:若文件不存在,则创建它,需要使用mode选项,用来指定新文件的访问权限。
		O_APPEND:追加写
		
返回值:
成功:新打开的文件描述符。
失败: - 1;
向一个文件中写入数据
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
	umask(0);//将所写的文件权限清空
	int fd = open("log.txt",O_WRONLY | O_CREAT,0644);
	if(fd < 0)
	{
		printf("open error");
		return 0;
	}
	const char* msg = "hello world\n";
	write(fd, msg , strlen(msg));//c++中不需要加1;
	close(fd);
}

三.文件描述符

1. 概念0 & 1 & 2
Linux下的进程默认会打开三个文件描述符,
标准输入0,
标准输出1,
标准错误2,
0,1,2对应的物理设备一般是:键盘,显示器,显示器。
文件指针(FILE*)在文件描述符的上级;

进程和文件的对应关系:一个进程可以打开多个文件。
而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数 组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件
在这里插入图片描述
重定向:
本来应该显示到显示器上的内容,当关闭显示器后,创建5个文件,那么5个文件的内容输出的话,全都输出到第一个文件中。
输出重定向的本质:先关闭1号文件,然后打开一个文件。
重定向只能把1号文件描述符内的文件内容重定向。
数据刷新方案缓冲方式:
1.无缓冲
2.行缓冲,显示器------》库函数
3.全缓冲,文件------》库函数

int main()
{
	const char * msg1 = "hello printf\n";
	const char * msg2 = "hello fwrite\n";
	const char * msg3 = "hello write\n";
	printf(msg1);
	fwrite(msg2,1,strlen(msg2),stdout);
	write(1,msg3,strlen(msg3));
	fork();
}

显示器上一共打印了5个消息,printf和fwrite是多打印的。
多打印两个这种操作肯定是和fork()有关系的。
printf和fwrite库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。缓冲区的内容,并没有立即刷新出来,在调用fork()之后,进程退出,会统一刷新,写入文件中。fork()之后,创建子进程,代码共享,数据通过写时拷贝的方式各自独有一份。子进程就会有了同样的一份数据,即产生两份数据。
write时系统调用接口,没有缓冲区,库函数底层时系统调用。

四.理解文件系统

1.要把文件管好,就要把磁盘管好。
硬盘分为机械硬盘(HDD)和固态硬盘(SSD);

当我们使用ls -l时除了看到文件名,还看到了文件元数据。
rwxr-xr-x. 1 root root 7493 “9月 13:15:56” a.out
属性-----硬链接数----文件所有者-----组----大小-----最后修改时间

inode:表示文件的属性。
创建一个文件,就要有一个inode号;

文件系统简单了解
Boot block--------Block group 0------------Block group n
Boot block是操作系统的启动区,如果这块出现错误,磁盘就不能使用。
超级块----------------inode节点表--------------数据区
超级块中描述了inode的使用空间和剩余空间以及数据区的使用空间和剩余空间。

inode bitmap:给inode节点表中分配格子。
block bitmap:给数据区分配格子。记录那个格子被占用,那个没有占用。
创建一个新文件主要的4个操作:
1.存储属性:
在内核中找到一个空闲的i节点,内核把文件信息记录到其中。
2.存储数据:
该文件需要储存几个磁盘块,就在内核中找到几个空闲的磁盘块,将内核缓冲区的内容复制上去。
3.记录分配情况
文件内容依次存放,内核在inode节点表中记录了上述块列表。
4.添加文件名到目录
新创建的文件名叫abc 内核将入口(263466,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性链接起来。
创建或者写文件:
在inode位图中找到一个空闲位图,将0置为1,并在对应的inode中写属性,其次将inode数据区中的空闲格子,由0置为1,并在数据区中写内容,最后将文件和映射关系放在目录中。
查文件
已知inode号为1234,其次在inode节点表中找1234,通过找到的inode位置找到数据区所在的位置,从第一个位置开始,读取文件大小个数据。
删除文件
找到文件的inode;inode位图清0,将数据位图请0;

目录也是文件,维护好文件名和inode之间的映射关系。

五.理解软硬链接

文件名:link
硬链接: link-h
硬链接没有独立的inode;
我们看到,真正找到磁盘上文件的并不是文件名,而是inode。
在linux下可以让多个文件名对应于同一个inode;
就像是c++中的引用计数一样,当计数为0时,则删除文件。
软连接 link-s:里面放的是文件的路径。
软链接时通过名字引用另一个文件。
通过软连接可以直接从顶层目录指向底层的目录。

面试题:
1.fopen和open的区别?

  1. fopen 系列是标准的C库函数;open系列是 POSIX 定义的,是UNIX系统里的system call。
    也就是说,fopen系列更具有可移植性;而open系列只能用在 POSIX 的操作系统上。
  2. 使用fopen 系列函数时要定义一个指代文件的对象,被称为“文件句柄”(file handler),是一个结构体;而open系列使用的是一个被称为“文件描述符” (file descriptor)的int型整数。
  3. fopen 系列是级别较高的I/O,读写时使用缓冲;而open系列相对低层,更接近操作系统,读写时没有缓冲。由于能更多地与操作系统打交道,open系列可以访问更改一些fopen系列无法访问的信息,如查看文件的读写权限。这些额外的功能通常因系统而异。
  4. 使用fopen系列函数需要"#include <sdtio.h>";使用open系列函数需要"#include <fcntl.h>" ,链接时要之用libc(-lc)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值