Linux——文件相关

目录

一、什么是文件

二、系统文件I/O

三、文件描述符fd

四、文件描述符的分配原则

五、重定向

六、dup2系统调用

七、FILE

八、理解文件系统

九、硬链接与软连接

1、硬链接

2、软连接 

十、动态库和静态库


一、什么是文件

文件 = 内容 + 属性

在Linux中有一个理解叫一切皆文件。

站在系统的角度,能够被input读或者能够output写出的设备就叫做文件。

狭义的文件:普通的磁盘文件

广义的文件:显示器,键盘,网卡,声卡,显卡几乎所有的外设都可以被称为文件。

什么是当前路径?

当一个进程运行起来的时候每个进程都会记录自己当前所处的工作路径。

C语言默认会打开三个输入输出流,分别是stdin,stdout,stderr

这三个流的类型都是FILE*,fopen返回值类型,文件指针

通过fopen打开文件的方式

r

Open text file for reading.

The stream is positioned at the beginning of the file.

r+

Open for reading and writing.

The stream is positioned at the beginning of the file.

w

Truncate file to zero length or create text file for writing.

The stream is positioned at the beginning of the file.

w+

Open for reading and writing.

The file is created if it does not exist , otherwise it is truncated.

The stream is positioned at the beginning of the file.

a

Open for appending(writing at end of file).

The file is created if it does not exist.

The stream is positioned at the end of the file.

a+

Open for reading and appending(writing at end of file).

The file is created if it does not exist.The initial file position for reading is at the beginning of the file,but output is always appended to the file.

二、系统文件I/O

除了C语言提供的文件相关接口,我们还可以采用系统接口来进行访问文件。

写文件


#include <stdio.h>
#include <sys/types.h>
#include <sys.stat.h>
#include <fcntl.h>
#include <unstd.h>
#include <string.h>

int main()
{
	umask(0);
	int fd = open("myfile", O_WRONLY | O_CREAT, 0664);
	if (fd < 0)
	{
		perror("open");
		return 1;
	}
	int count = 5;
	const char* msg = "hello world\n";
	int len = strlen(msg);
	while (count--)
	{
		write(fd, msg, len);
	}
	close(fd);
	return 0;
}

读文件

#include <stdio.h>
#include <sys/types.h>
#include <sys.stat.h>
#include <fcntl.h>
#include <unstd.h>
#include <string.h>

int main()
{
	int fd = open("myfile", O_RDONLY);
	if (fd < 0)
	{
		perror("open");
		return 1;
	}
	const char* msg = "hello world\n";
	char buf[1024];
	while (1)
	{
		ssize_t s = read(fd, buf, strlen(msg));
		if (s > 0)
		{
			printf("%s", buf);
		}
		else
		{
			break;
		}
	}
	close(fd);
	return 0;
}

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_RDER:读写打开

        上面三个是常量,必须指定一个且只能指定一个

        O_CERAT:若文件不存在,则创建它。需要使用mode选项,来指明文件的访问权限

        O_APPEND:追加写

返回值:

        成功:新打开的文件描述符

        失败:-1

fopen,fclose,fread,fwrite都是C标准库中的函数,称之为库函数

open,close,read,write,lseek都属于系统提供的接口,称之为系统调用接口

可以认为f#系列的函数,都是对系统调用的封装,方便二次开发。

三、文件描述符fd

通过对open系统调用的学习我们了解到open的返回值为打开文件的文件描述符,是一个小整数。

        通过上面的代码和结果我们可以看到新打开的文件的文件描述符是3,那么0,1,2是什么呢?

0&1&2

        ·Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2.

        ·0,1,2对应的物理设备一般是:键盘,显示器,显示器 

        文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行的open系统调用,所以必须让进程和文件关联起来。每个进程结构体内都有一个指针*file,指向一张表files_struct,该表是最重要的部分就是包含了一个指针数组,每个元素都是指向一个打开文件的指针!所以,本质上文件描述符就是该数组的下标。只要拿到文件描述符,就可以找到对应的文件。

四、文件描述符的分配原则

从上面的代码可以了解到新打开的文件,文件描述符就是3。当关闭0或是2时在看看时什么结果。

 

        通过代码我们发现当关闭0时,新打开的文件的文件描述符就是0。可见,文件描述符的分配规则:在files_struct数组中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

五、重定向

 

        通过上面的代码和结果,我们发现,本来应该输出到显示器的内容,输出到了文件myfile中,其中fd = 1。这种现象叫做输出重定向。 

六、dup2系统调用

 

函数原型如下:

#include <unistd.h>

int dup2(int oldfd, int newfd);

        该函数的功能是将newfd的描述符重新向到oldfd中去,相当于重定向后都是操作oldfd操作的文件。在过程中如果newfd已经有对应打开的文件信息,则会先关闭文件后再重定向。

七、FILE

        因为IO相关函数与系统调用接口对应,并且库函数封装了系统调用,所以本质上,访问文件都是通过fd访问的。所以C库中的FILE结构体内部,必定封装了fd。

分析如下代码

运行结果

用./a.out > file.txt结果

        通过上面的结果我们发现printf和fwrite(库函数)都输出了两次,而write只输出了一次(系统调用)。原因肯定和fork有关。

        ·一般C库函数写入文件时是全缓冲,而写入显示器是行缓冲。

        ·printf,fwrite库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式从行缓冲变成了全缓冲。

        ·而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后。

        ·但是进程退出之后,会同一刷新,写入文件当中。 

        ·fork的时候,父子数据会发生写时拷贝,所以当父子进程准备刷新的时候,子进程也就是有了同样的一份数据,随机产生了两份数据。

        ·write没有变化,说明没有所谓的缓冲区。

        综上所述:printf和fwrite库函数会自带缓冲区,为write系统调用没有带缓冲区。另外,我们这里说的缓冲区都是用户级别的缓冲区。目的是为了提升整机性能,操作系统也会提供相关内核级别缓冲区。printf, fwrite是库函数,write是系统调用,库函数在系统调用的“上层”,是对系统调用的封装,但是write没有缓冲区,而printf和fwrite有,就说明,该缓冲区是二次加上的,又因为是C语言,所以由C标准库提供。

缓冲区的刷新策略

1、立即刷新

2、行刷新(行缓冲)

3、满刷新(全缓冲)

特殊情况

1、用户强制刷新(fflush)

2、进程退出

八、理解文件系统

在Linux中使用ls -l指令会看到如下结果

从左向右依次是:模式,硬连接数,文件所有者,组,大小,最后修改时间,文件名

在上面的文件描述符中提到了一个inode的概念。

要解释清楚inode我们先简单了解以下文件系统。

        上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个block。一个block的大小是由格式化的时候决定的,并且不可以更改。

        虽然磁盘的基本单位是扇区(512字节),但是操作系统(文件系统)和磁盘进行IO的基本单位是4kb。

        Linux在磁盘上存储文件的时候,将内容和属性是分开存储的。 

        Block Group:文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。

        super block:存放文件系统本身的结构信息。记录的新的主要有:block和inode的总量,未使用的block和inode数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统相关信息。super block的信息被破坏,可以说整个文件系统结构就被破坏了。

        GDT,group descriptor table:块组描述符,描述块组属性信息。

        block bitmap:block bitmap中记录这data block中哪个数据块已经被占用,哪个数据块没有被占用。

        inode bitmap:每个bit表示一个inode是否空闲可用。假设有10000+个blocks,10000+比特位,比特位和block是一一对应的,其中比特位为1表示该block被占用,否则表示可用。

        inode table:inode是一个128字节的空间,保存的是对应文件的属性,该块组内,所有文件的inode空间的集合需要标识唯一性,每个inode块,都需要一个inode编号!一般而言一个文件,一个inode,一个inode编号。

        data block:存放文件内容。

        通过上面了解到一个文件对应一个inode编号,那么我们在创建文件时用的文件名又是怎样存储的呢?

        文件名是存在目录中的,目录也是文件有自己的inode有自己的data block,文件名和inode编号的映射关闭互为key值。

创建一个新文件主要有以下四个操作:

1、存储属性

        内核先找到空闲的inode(假设这里是263446).内核把文件信息记录到其中。

2、存储数据

        该文件需要存储在某几个磁盘块,内核找到这几个磁盘块。将内核缓冲区的第一块数据复制到第一个空闲块,以此类推。

3、记录分配情况

        内核在inode上的磁盘分布记录了上述块列表。

4、添加文件名到目录

        新的文件名叫abc。Linux内核将入口(263446,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。

九、硬链接与软连接

1、硬链接

        我们看到,真正找到磁盘上的文件的并不是文件名,而是inode。在Linux中可以让多个文件名对应同一个inode。

        abc.txx和def.txt和ghi.txt的链接状态完全相同,他们被称为指向文件的硬链接。内核记录了这个连接数,inode2491322的硬链接数位3.

        我们在删除文件时干了两件事:1.在目录中将对应的记录删除,2.将硬链接数-1,如果为0,则将对应的磁盘释放。

        创建硬链接不是真正的创建新文件就是在指定的目录下建立了文件名和指定inode的映射关系。默认创建目录引用计数(硬链接)为何是2,因为自己的目录名inode和自己目录内部. inode。

2、软连接 

硬链接是通过inode引用另外一个文件,软连接是通过名字引用另外一个文件。

软连接应用:相当于Windows下的快捷方式。特性:可以理解为软连接的文件内容是指向的文件对应的路径。

软硬连接有什么本质区别:有没有独立的inode

软连接有独立的inode:软连接是一个独立的文件

硬链接没有独立的inode:硬链接不是一个独立的文件

解释以下acm三个时间

access:最后访问时间

modify:文件内容最后修改时间

change:属性最后修改时间

十、动态库和静态库

        静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候不再需要静态库。

        动态库(.so):程序在运行的时候才链接动态库的代码,多个程序共享使用库的代码。

        一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

        在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。

        动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程公用,节省了内存和磁盘空间。

1、生成静态库

现在有两个源文件用c语言实现的两个功能,还有这两个源文件的头文件。

 

生成静态库的指令(写在Makefile)

 

编译可执行文件时

gcc main.c -I 指定头文件路径 -L 指定库路径 -l指定库名

目标文件生成之后,静态库删除,程序照样可以执行。

2、生成动态库

shared:表示生成共享库格式

fPIC:产生位置无关码

库名规则:libxxx.so

示例:

运行动态库

(1)拷贝.so文件到系统共享库路径下,一般指/usr/lib

(2)更改LD_LIBRARY_PATH

 

(3)ldconfig 配置/etc/ld.so.conf.d/xxxx.conf 

注:

如果我们只有静态库,没办法gcc只能针对该库进行静态链接

如果动静态库同时存在,默认用的就是动态库

如果动静态库同时存在,但是我们要用静态库呢?-static,它的意义就是摒弃默认优先使用静态库原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值