站在山顶和站在山脚下的两个人,虽然地位不同,但在对方眼里,同样的渺小!
目录
- 6. 基础IO
- 6.1 标准库IO接口
- 6.1.1 fopen (r/r+/w/w+/a/a+///FILE *fopen(const char *path, const char *mode);
- 6.1.2 size_t fwrite(void *ptr, size_t size, size_t nmemb,FILE *stream);
- 6.1.3 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 6.1.4 int fseek(FILE *stream, long offset, int whence);
- 6.1.4 int fclose(FILE *fp);
- 6.1.5 printf,sprintf, fprintf
- 6.1.6 fgets
- 6.1.7 fputs
- 6.2 系统调用接口
- 6.3 文件描述符
- 6.4 重定向
- 6.5 文件系统
- 6.6 库的生成
- 6.7 库的使用
6. 基础IO
6.1 标准库IO接口
6.1.1 fopen (r/r+/w/w+/a/a+///FILE *fopen(const char *path, const char *mode);
1. 参数解析
- path: 文件路径名
- mode:
r 只读打开已经存在的文件
r+ 读者打开已经存在的文件
w 只写打开文件,若文件不存在则创建,存在则长度截为0
w+ 读写打开文件,若文件不存在则创建,创建的文件权限默认为664;
若文件存在则长度截为0
a 追加写打开,若文件不存在则创建,每次总是写到文件末尾
a+ 读写(追加),文件读写位置刚打开的时候在起始位置,当写入的时候文件读写位置移动到文件末尾;若文件不存在,则创建文件。
2. 返回值
- 文件的操作句柄----文件流指针
6.1.2 size_t fwrite(void *ptr, size_t size, size_t nmemb,FILE *stream);
1. 参数解析
- ptr:要写入的数据
- size:要写入的块大小 —fwrite对数据进行按块操作
- nmemb:要写入的块个数
- stream:fopen返回的文件流指针
2. 返回值
- 实际写入的块个数
6.1.3 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
1. 参数解析
- ptr: 用于接收数据的缓冲区
- size:要读取的块大小
- nmemb:要读取的块个数
- stream:文件流指针
2. 返回值
- 实际读取到的块个数 文件末尾:0
6.1.4 int fseek(FILE *stream, long offset, int whence);
1. 参数解析
- whence:跳转的起始位置
- SEEK_SET 文件起始位置
- SEEK_CUR 当前读写位置
- SEEK_END 文件末尾位置
- offset: 偏移量
2. 返回值 - int型
6.1.4 int fclose(FILE *fp);
review.c
/*===============================================================
* Copyright (C) . All rights reserved.")"
* 文件名称:
* 创 建 者: yangjie
* 创建日期: 2020-1-6
* 描 述:FILE *fopen(const char *path, const char *mode);
* size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
* size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
* int fseek(FILE *stream, long offset, int whence);
* int fclose(FILE *stream);
* =============================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// FILE *fopen(const char *path, const char *mode);
// path: 文件路径名
// mode:
// r 只读打开 r+ 可读可写
// w 只写,文件不存在则创建, 若文件存在则清空原有内容
// w+ 相较于w, 多了可读操作
// a 追加写打开, 若文件不存在则创建, 每次总是写到文件末尾
// a+ 相较于a, 多出了可读操作
// 返回值: 文件的操作句柄----文件流指针
FILE* fp = fopen("tmp.txt", "w+");
if(fp == NULL)
{
perror("fopen errror!");
return -1;
}
// size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
// ptr: 要写入的元素
// size: 要写入的块大小,--fwrite对数据进行按块操作
// nmemb: 要写入的块个数
// stream: fopen返回的文件流指针
// fseek(fp,10,SEEK_END);
char* ptr = "evething would be better!!!\n";
size_t ret = fwrite(ptr,strlen(ptr),1,fp); // size_t 无符号的32位整数
if(ret == 0)
{
perror("fwite errror!");
return -1;
}
// int fseek(FILE *stream, long offset, int whence);
// 对stream文件的读写位置跳转到whence位置偏移offset个字节处
// whence: 跳转的起始位置
// SEEK_SET 文件起始位置
// SEEK_CUR 当前读写位置
// SEEK_END 文件末尾位置
// offset: 偏移量
fseek(fp,0,SEEK_SET);
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// ptr: 用于接收数据的缓冲区
// size: 要读取的块大小
// nmemb: 要读取的块个数
// stream: 文件流指针
// 返回值: 实际返回的块个数
char buf[1024] = {0};
ret = fread(buf,1023,1,fp);
perror("fread error!");
printf("ret: %d-[%s]\n",ret,&buf[2]);
// int fclose(FILE *stream);
fclose(fp);
return 0;
}
运行结果
[dev@localhost baseio]$ ./review
fread error!: Success
ret: 0-[ething would be better!!!
]
[dev@localhost baseio]$
6.1.5 printf,sprintf, fprintf
- printf(格式化字符串,输出到终端)
- sprintf(格式化字符串,将格式化后的字符串写入到一个buff中)
- fprinf(格式化字符串,将格式化后的子串写入到文件中)
6.1.6 fgets
6.1.7 fputs
6.2 系统调用接口
6.2.1 int open(const char *pathname, int flags, mode_t mode);
1. 参数解析
- pathname: 要打开或创建的目标文件
- flags: 标志选项
- 参数:
必须选项(这三个常量,必须指定一个且只能指定一个)
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
非必须选项
O_CREAT: 若文件存在则打开,不存在则创建;
O_APPEND: 追加写;
O_TRUNC:若文件存在则将文件长度截为0;
O_EXCEL:与O_CREAT同时使用,若文件存在则报错。 - mode:若文件不存在,需要创建的时候,用于指定创建文件权限
2. 返回值 - 成功:新打开的文件描述符
- 失败:返回-1
6.2.2 ssize_t read(int fd, void *buf, size_t count);
1. 参数解析
- fd:open打开文件所返回的文件描述符
- count:要写入的字节数
- buff:要写入的数据
2. 返回值
- 成功:实际写入的字节数
- 失败:返回-1
6.2.3 ssize_t write(int fd, const void *buf, size_t count);
1. 参数解析
- fd:open打开文件所返回的文件描述符
- count:要写入的字节数
- buff:要写入的数据
2. 返回值
- 成功:实际写入的字节数
- 失败:返回-1
6.2.4 off_t lseek(int fd, off_t offset, int whence);
1. 参数解析
- 跳转fd文件的读写位置到指定处
whence:SEEK_SET SEEK_CUR SEEK_END - offset:偏移量
2. 返回值 - 成功:实际写入的字节数
- 失败:返回-1
6.2.5 int close(int fd);
systemio.c
/*===============================================================
* Copyright (C) . All rights reserved.")"
* 文件名称:systemio
* 创 建 者: yangjie
* 创建日期: 2020-1-6
* 描 述: 这个demo用于演示系统调用io接口的基本使用
* int open(const char *pathname, int flags, mode_t mode);
* ssize_t read(int fd, void *buf, size_t count);
* ssize_t write(int fd, const void *buf, size_t count);
* off_t lseek(int fd, off_t offset, int whence);
* int close(int fd);
* mode_t umask(mode_t mask);
* =============================================================*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
// mode_t umask(mode_t mask);
// 将当前进程的文件创建权限掩码修改为mask
umask(0);
// int open(const char *pathname, int flags, mode_t mode);
int fd = open("test.txt",O_RDWR|O_CREAT|O_APPEND,0664);
if(fd < 0 )
{
perror("open error!");
return -1;
}
// ssize_t read(int fd, void *buf, size_t count);
// fd: open打开文件所返回的文件描述符
// buf: 要写入的数据
// count: 要写入的字节数
// 返回值: 实际写入的字节数
char* data = "life is the sun!!!\n";
ssize_t ret = write(fd, data, strlen(data));
if(ret < 0)
{
perror("write error!");
return -1;
}
// off_t lseek(int fd, off_t offset, int whence);
// 跳转fd文件的读写位置到指定处
// whence: SEEK_SET SEEL_CUR SEELK_END
// offset: 偏移量
lseek(fd,0,SEEK_SET);
// ssize_t read(int fd, void *buf, size_t count);
// 从fd文件中读取count长度的数据, 放到buf中
// 返回值: 返回实际读取到的字节数 失败: 返回-1
char buf[1024] = {0};
ret = read(fd, buf, 1023);
if(ret < 0)
{
perror("read error!");
return -1;
}
printf("ret: %d-[%s]\n",ret,&buf);
// int close(int fd);
close(fd);
}
运行结果
[dev@localhost baseio]$ ./systemio
ret: 39-[fd:1
Mind and Hand!
life is the sun!!!
]
[dev@localhost baseio]$
6.3 文件描述符
6.3.1 概念
文件描述符:系统调用IO接口的操作句柄int,实际上是一个数字。
文件流指针:标准IO接口的操作句柄 FILE*。
6.3.2 文件描述符与文件流指针的关系
- 文件流指针中封装了文件描述符,文件流指针结构体中包含了一个成员就是我们的文件描述符,文件流指针中还定义了一个缓冲区,我们所说的换行刷新缓冲区,实际上就是刷新的这个缓冲区,这个缓冲区用于将短小数据合成大数据一次性写入文件,这样可以提高效率,系统调用接口没有这个缓冲区。
6.3.3 LInux下3个缺省打开的文件描述符
标准输入:0
标准输出:1
标准错误:2
- 对应的物理设备一般是:键盘、显示器、显示器
6.3.4 文件描述符的分配原则
(1)在 files_struct 数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符;
(2)最小未使用原则;
(3)分配有限。
6.4 重定向
6.4.1 概念
- 重定向指的是文件描述符的重定向,文件描述符这个下标中的文件描述信息,从一个文件变成另外一个文件,这时候用户通过文件描述符项文件写入数据的时候,文件描述符并没有改变依然还是相同的数字,但是因为描述符下标中的文件描述信息的改变,而导致数据流向另外一个文件。
6.4.2 功能
- 改变数据的流向。
6.4.3 重定向的分类
>>追加重定向
O_APPEND(向原有内容下方添加新内容)
>清空重定向
O_TRUNC(清空原有内容分后添加新内容)
<标准输入重定向
filedesc.c
/*===============================================================
* Copyright (C) . All rights reserved.")
* 文件名称:
* 创 建 者:yangjie
* 描 述:体会文件描述符分配规则
================================================================*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
close(1);
int fd = open("./test.txt", O_CREAT|O_RDWR, 0664);
if(fd < 0) {
perror("open error");
return -1;
}
printf("fd:%d\n", fd);
fflush(stdout);
close(fd);
return 0;
}
运行结果
[dev@localhost baseio]$ ./filedesc
[dev@localhost baseio]$ cat test.txt
fd:1
Mind and Hand!
cat test.txt
cat tmp.txt
[dev@localhost baseio]$ cat test.txt
fd:1
Mind and Hand!
life is the sun!!!
[dev@localhost baseio]$ cat tmp.txt
evething would be better!!!
6.4.4 使用dup2系统调用
(1)接口说明
- int dup2(int oldfd, int newfd);
(2)功能 - 文件复制。
6.5 文件系统
6.5.1 说明
- Linux下的文件系统以 ext3/4 为主,主要是组织硬盘如何存储一个文件。
6.5.2 磁盘文件系统的组成
- (1)数据块(存储数据的区域)
- (2)iNode节点区域(存储文件的描述信息,如文件大小,所有者,最近修改时间等)
- (3)iNode bitmap (inode 节点分配剩余情况,每个bit表示一个inode是否空闲可用)
- (4)Block bitmap (数据块剩余情况)
- (5)超级块(文件系统的记录信息)
- (6)Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成,政府管理各区的例子;
- (7)GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
6.5.3 软链接/硬链接
(1)软链接
- 独立的文件,功能类似于windows下的快捷方式
- ln -s 文件名(生成软链接文件)
(2)硬链接
- 一个文件的目录项/别名
- ln 文件名(生成硬链接文件)
(3)软链接与硬链接的区别
- 软连接文件是一个单独的文件,数据中存储源文件路径;硬链接文件是一个文件别名(目录项),与源文件操作同一个inode节点;
- 软连接可以对目录创建,硬链接则不可以;
- 软连接可以跨区建立,硬链接则不可以;
- 删除源文件,软连接文件失效,硬链接文件无影响(链接数-1)。
6.5.4 文件的存储流程
- (1)通过 databitmap 快速找到空闲的数据块,存储数据;
- (2)并且通过 inode bitmap 快速找到一个空间的 inode节点,保存文件的源信息(名称、大小、权限、数据的存储位置…);
- (3)将文件的(inode节点号+文件名)目录项保存到所在目录文件中;
6.5.5 文件的查找流程
- (1)通过目录项(文件名称+文件对应的 inode节点号)快速找到文件的 inode节点号;
- (2)通过 inode节点号可以在inode table 中快速的找到文件的 inode 节点;
- (3)通过 inode节点可以找到文件数据存储位置,进而获取到数据 inode节点(文件的描述信息,其中有文件数据的存储位置);
6.6 库的生成
6.6.1 动态库
(1)定义
- 程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
(2)动态库的生成
gcc -fPIC -c main.c -o main.o
fPIC(生成目标代码时产生位置无关代码)
gcc --share main.o -o libmain
–share(要生成的是动态库而不是可执行程序)
(3)库文件
- 库文件实际上就是封装了一大堆已经编译完成的代码文件,通过链接这个库进而获取到响应函数而实现。
(4)动态库命名
lib是前缀
.so是后缀
中间是库名称
6.6.2 静态库
(1)定义
- 程序在编译链接的时候把库的代码链接到可执行文件中。
(2)动态库的生成
gcc -c main.c -o main .o
ar -cr lib main.a main.o
(3)静态库生成的链接器
-c(创建静态库)
-f(模块替换)
(4)静态库的命名
- lib是前缀
- .so是后缀
- 中间是库名称
6.7 库的使用
6.7.1 动态库的使用
将库文件放到指定的路径下
/lib/lib64/ usr/lib…
设置环境变量
LIBRARY_PATH
l
链接动态库,只要库名即可(去掉lib以及版本号)
L
链接库所在的路径.
示例: gcc main.o -o main –L. -lhello
6.7.2 运行动态库
前提: 动态库使用
将库文件放到指定的路径下
/lib/lib64/ usr/lib…
设置环境变量
LIBRARY_PATH
ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新