fopen fread fwrite fseek fclose
1 /*回顾标准库IO接口*/
2
3 #include <stdio.h>
4 #include <string.h>
5
6 int main()
7 {
8 FILE *fp = NULL;
9 fp = fopen("./tmp.txt", "w+");
10 if (fp == NULL) {
11 perror("fopen error");
12 return -1;
13 }
14
15 char buf[1024] = "jintianrenhaoshao~~\n";
16 //size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
17 //返回值为 nmemb而不是size
18 fwrite(buf, strlen(buf), 1, fp);
19
20 //int fseek(FILE *stream, long offset, int whence);
21 //对fp文件读写位置从whence开始偏移offset个字节
22 // whence:
23 // SEEK_SET 从文件起始位置开始偏移
24 // SEEK_CUR 从当前读写位置开始偏移
25 // SEEK_END 从文件末尾位置开始偏移
26 // 返回值:从文件起始位置到当前跳转位置的偏移量
27 fseek(fp, 0, SEEK_SET);
28 //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
29 memset(buf, 0x00, 1024);
30 int ret = fread(buf, 1, 1023, fp);
31 if (feof(fp)) {
32 printf("at end of file\n");
33 }else {
34 perror("fread error");
35 }
36 printf("ret:%d--buf:[%s]\n", ret, buf);
37 fclose(fp);
38 return 0;
39 }
文件结构体
struct FILE
{
char *_ptr;//文件输入的下一个位置
int _cnt;//当前缓冲区的相对位置
char *_base;//指基础位置(文件的起始位置)
int _flag;//文件标志
int _file;//文件的有效性验证
int _charbuf;//检查缓冲区状况,如果缓冲区则不读取
int _bufsiz;//文件的大小
char *_tmpfname;//临时文件名
};
文件描述符:
进程通过struct file_struct 结构体来描述打开的文件----------------------使用struct file* fd_array[],文件描述符就是数组的下标
用户打开文件,操作系统通过file结构体描述文件,并将指针添加到fd_array[]中。向用户返回这个文件描述信息在数组下标中的位置,用户操作文件的时候,将下标床给操作系统,系统通过下标找到文件
进程的描述符是有上限的
文件描述符的分配原则:最小未使用原则
一个进程在运行起来默认会打开三个文件:
- 标准输入 0(文件描述符) stdin(文件流指针)
- 标准输出 1(文件描述符) stdout(文件流指针)
- 标准错误 2(文件描述符) stderr(文件流指针)
1 /*系统调用接口使用*/
2
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <fcntl.h>
7
8 int main()
9 {
10 //mode_t umask(mode_t mask);
11 //设置调用进程的文件创建权限掩码
12 umask(0);
13 //int open(const char *pathname, int flags, mode_t mode);
14 // pathname: 要打开的文件路径名
15 // flags: 选项参数
16 // 必选其一:
17 // O_RDONLY 只读
18 // O_WRONLY 只写
19 // O_RDWR 读写
20 // 可选项:
21 // O_CREAT 文件存在则打开,不存在则创建
22 // O_TRUNC 将文件长度截断为0(清空原有内容)
23 // O_APPEND 追加
24 // mode: 权限 0664
25 // (mode & ~umask)
26 // 返回值:文件描述符(正整数)(系统调用接口的操作句柄)失败:-1
27 int fd = open("./test.txt", O_RDWR | O_CREAT | O_APPEND | O_TRUNC, 0664);
28 if (fd < 0) {
29 perror("open error");
30 return -1;
31 }
32 char *ptr = "jintianzhilaile 20 ren\n";
33 //ssize_t write(int fd, const void *buf, size_t count);
34 // fd: open返回的文件描述符
35 // buf: 要写入的数据
36 // count: 要写入的数据长度
37 // 返回值:实际写入的数据长度(字节) 失败:-1
38 int ret = write(fd, ptr, strlen(ptr));
39 if (ret < 0) {
40 perror("write error");
41 return -1;
42 }
43
44 //off_t lseek(int fd, off_t offset, int whence);
45 // fd: open返回的文件描述符
46 // offset: 偏移量
47 // whence:偏移起始位置
48 // SEEK_SET 文件起始位置
49 // SEEK_CUR 当前读写位置
50 // SEEK_END 文件末尾位置
51 // 返回值:从文件起始位置到当前读写位置的偏移量 失败:-1
52 lseek(fd, 0, SEEK_SET);
53
54 char buf[1024] = {0};
55 //ssize_t read(int fd, void *buf, size_t count);
56 // fd: open返回的文件描述符
57 // buf: 内存首地址,用于存储读取的数据
58 // count: 要读取的数据长度
59 // 返回值:实际读取的数据长度(字节) 失败:-1
60 ret = read(fd, buf, 1023);
61 if (ret < 0) {
62 perror("read error");
63 return -1;
64 }
65 printf("read buf:[%d-%s]\n", ret, buf);
66
67 //int close(int fd);
68 close(fd);
69 return 0;
70 }
文件流指针和文件描述符比较:
- 库函数的操作句柄是文件流指针
- 系统调用接口的句柄是文件描述符
进程运行在用户态/内核态:
用户态切换到内核态:发起系统调用
重定向:
改变数据流向,将写入(newfd)指定文件的数据,改变写入到(oldfd)另一个文件当中
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4
5 int main()
6 {
7 //close(1);
8
9 int fd = open("./test.txt", O_RDWR | O_CREAT);
10 if (fd < 0) {
11 perror("open error");
12 return -1;
13 }
14 //将要打印的数据,不写入标准输出,而是写入test.txt
15 dup2(fd, 1);
16 //\n刷新缓冲区,仅仅针对标准输出文件才有效
17 //其它文件,\n仅仅具备换行效果
18 printf("fd:%d\n", fd);//stdout -> 1 ---> test.txt
19 //fflush(stdout);
20
21 //close是系统调用接口,关闭文件时不会刷新缓冲区(因为我们所说的这个 缓冲区是stdout的缓
冲区(用户态的缓冲区))
22 close(fd);
23 return 0;
24 }
文件系统
在linux下为了更好的对文件管理,所以要对文件进行分区。每一个文件系统分区当中会将磁盘划分为数据块。
文件存储过程:通过inode_bitmap在inode区域获取空闲inode节点,通过data_bitmap获取空闲数据块,在inode节点中记录文件信息以及数据块位置,并且将文件数据写入到数据块中,将自己的目录项添加到所在目录项当中。
目录文件:文件中记录着目录下的文件信息(文件名+inode节点)-----目录项
文件读取过程:在目录项当中通过文件名获取inode节点(文件唯一),通过inode节点号在inode节点区域找到inode节点,通过inode节点中数据块地址信息,在指定数据块中读取数据。
查看文件大小:sudo dump2fs -h /dev/sda1 | grep " lnode size"
软连接/硬链接
软链接文件: ln -s srcfile destfile (就像文件的快捷方式,是一个独立的文件)
硬链接文件: ln srcfile destfile (一个文件的名字(目录项),与源文件公用一个inode节点)
区别:
1.删除源文件,软链接文件将失效,硬链接无影响(链接数 -1)
2.软链接可以跨分区创建,硬链接不可以
3.软链接可以对目录创建,硬链接不可以
动态库/静态库: 生成----------------------------------二进制指令的集合
设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。
设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。
gcc编译过程: 预处理 ,编译, 汇编 ,链接
-fPIC 产生位置无关代码 -l (指定要链接的库名称)
--share 指定gcc生成动态库而不是可执行程序
动态库的生成:命名:lib(前缀) .so(后缀) 中间是库名称
gcc -fPIC -c child.c -o child .o
gcc --share child.o -o libmychild.so
静态库的生成:命名:lib(前缀) .a(后缀) 中间是库名称
gcc -c child.c -o child .o
ar -cr libmychild.a child.o -c(创建) -r(模块替换)
动态库的使用
解决办法:
- 将我们生成的动态库添加到系统默认搜索库的文件中系统中 (/lib64)或者(/usr/lib/)这两个都可以。
2.设置设置动态库运行加载的环境变量 export LD_LIBRARY_PATH=.
静态库的使用
注意事项:
想要使用第三方静态库,不能使用-static 选项,因为static是生成静态链接可执行程序,所有的库都是用静态库,因为标准库时动态库所以在链接时会出错,因此只需要将第三方静态库拷贝到是定路径下,使用 -L选项指定链接搜索路径,那么这个时候链接的就是静态库;