Linux - 基础 IO
文件的宏观理解:
狭义理解:
广义理解:
文件操作的归类认知:
系统角度:
int fputs(const char *s, FILE *stream);
fputs 函数是将 s 所指向的数据往 stream 中所指向的文件中写
char * fgets ( char * str, int num, FILE * stream )
注:
fwrite 的使用方法
注:
#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: 追加写
返回值:
成功:新打开的文件描述符
失败:-1
注:
注:write read close lseek…… 与 C 语言文件相关接口用法类似
注:
文件描述符就是从 0 开始的小整数。当打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了 file 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针 files_struct*, 指向一张表 files_struct, 该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。只要拿着文件描述符,就可以找到对应的文件
补充:
总结:
补充:程序替换的时候不会影响重定向对应的数据结构的数据(程序替换影响的是进程虚拟地址空间部分,而重定向影响的是 files_struct 部分)
#include <unistd.h>
int dup2(int oldfd, int newfd);
注:
因为 IO 相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过 fd 访问的。因此 C 库当中的 FILE 结构体内部,必定封装了 fd
typedef struct _IO_FILE FILE; 在/usr/include/stdio.h
在/usr/include/libio.h
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; //封装的文件描述符
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
总结:
底层对应的文件描述符下标
应用层 C 语言提供的缓冲区数据
所谓的默认打开文件,标准输入、标准输出、标准错误其实是由底层系统支持的,默认一个进程在运行的时候,就打开了 0,1,2
一般 C 库函数写入文件时是全缓冲的,而写入显示器是行缓冲。printf fprintf 等库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。而我们放在缓冲区中的数据,就不会被立即刷新,甚至 fork 之后但是进程退出之后,会统一刷新,写入文件当中。但是 fork 的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。write 没有变化,说明没有所谓的缓冲 printf fputs 等 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS 也会提供相关内核级缓冲区。printf fprintf 是库函数, write 是系统调用,库函数在系统调用的 “上层”, 是对系统调用的 “封装”,但是 write 有内核级缓冲区,而 printf fwrite fputs 等缓冲区是用户级缓冲区,由 C 标准库提供
注:系统调用函数与库函数尽量不要混在一起使用,可能会与统一使用的函数的运行结果有所差异
文件:打开的文件、普通未打开的文件 打开的文件:属性与操作方法的表现就是 struct file{} 属于内存级文件 普通未打开的文件:磁盘上面未被加载到内存的 文件系统功能:将上述的这些文件管理起来
磁盘是计算机主要的存储介质,可以存储大量的二进制数据,并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘(Floppy Disk,简称软盘),如今常用的磁盘是硬磁盘(Hard disk,简称硬盘)。
补充:
我们可以将磁盘想象成磁带(线性结构),将磁盘看成一个线性空间(数组),类型为扇区的数组、数组个数为 10 亿多
这样划分就不用让 OS 读取数据时在哪个盘面、哪个磁道、哪个扇区找了,OS 与磁盘映射关系可以通过磁盘驱动来完成,这样也就做到强解耦性。无论换机械硬盘还是固态硬盘,OS 都不用改变读取磁盘数据的数据结构,只需改变磁盘的驱动程序即可
注:操作系统读取磁盘数据时的下标——LBA
Linux ext2 文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的 block。一个 block 的大小是由格式化的时候确定的,并且不可以更改。例如 mke2fs 的 - b 选项可以设 定 block 大小为 1024、2048 或 4096 字节。而启动块(Boot Block)的大小是确定的,
注:
总结:
创建一个新文件主要有一下 4 个操作:
大多是操作系统在同一个目录下是不允许存在同名文件的 删除文件不需要清空该文件占据的所有的空间数据(只需将该文件的 inode 和对应的数据块无效化即可(文件对应 inode 和 Block 位图中的数字 1 设置为 0,并将该文件所对应的目录中的数据块的关于该文件内容清空即可) Linux 下属性和内容是分离的,属性 inode 保存的(在同一块块组 inode 编号是不同的,但是跨组的 inode 编号可能相同),内容 Data blocks 保存的
补充:
硬链接: 硬链接的应用场景:方便进行相对路径的路径的设置
因此,可以看出.、… 的底层实现是通过硬链接的方式来实现的 注:
软链接: 注:硬链接是通过 inode 引用另外一个文件,软链接是通过名字引用另外一个文件
总结:软硬链接的区别:本质是是否是独立文件,有无独立 inode;用途:软链接可以指向特定的文件方便进行快速索引,硬链接是能进行相对路径设置
补充:
总结:
文件的 ACM 的应用场景:
注:
总结:
[root@localhost linux]# ls
add.c add.h main.c sub.c sub.h
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o
生成静态库
[root@localhost linux]# ar -rc libmymath.a add.o sub.o
ar是gnu归档工具,rc表示(replace and create)
查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o
t:列出静态库中的文件
v:verbose 详细信息
[root@localhost linux]# gcc main.c -I -L. -lmymath
-L 指定库路径
-I 指定头文件路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行
注:
<span style="font-size: 15px;">-I
:告诉 gcc 除了默认路径(<span style="font-size: 15px;">/usr/include
)以及当前路径之外,在指定路径下也找一下头文件<span style="font-size: 15px;">-L
:告诉 gcc 除了默认路径 (<span style="font-size: 15px;">/lib/ 、/lib64 、/lib64/libc*
) 以及当前路径之外,在指定路径下也找一下库文件<span style="font-size: 15px;">-l
库名称:具体链接哪个库C 语言编译时直接编译不用任何选项:
当自己的可执行程序编译时不想用这些选项:将头文件和库文件分别拷贝到默认路径下——库的安装(第三方库)(使用时必须带上 - l 库名称) 当只有静态库时,没有动态库,用 gcc 编译(不加 - static)会直接用静态链接生成可执行程序
补充:
补充:
1.拷贝. so 文件到系统共享库路径下, 一般指 / usr/lib
2.更改 LD_LIBRARY_PATH(当系统重启时使用之前添加的是无效的,应重新添加)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:路径
3.ldconfig 配置 / etc/ld.so.conf.d/,ldconfig 更新
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8