在学习C语言的阶段里,学过了文件操作——(C语言文件操作)Linux里,“一切皆文件的思想” ,更是围绕着文件进行操作。下面来说说Linux下的文件操作。
目录
-
《一》系统调用接口
在C语言里的fopen fread fwrite fclose都是C标准库的的函数——库函数
但是Linux的IO操作的open close read write lseek 是系统调用接口。这是前面说《进程》的时候一个图。
以下接口要带上<fcntl.h>这个头文件
- 接口1>int open(const char * pathname,int flags, mode_t mode) 返回值是文件描述符
pathname:文件名称
flags:参数选项/控制字:它们用 | 隔开。
必选
O_RDONLY 以只读方式打开文件
O_RDWR 以可读写方式打开文件
O_WRONLY 以只写方式打开文件
可选
O_APPEND 读写文件从文件尾部开始移动,所写入的数据追加到文件尾 重定向>>
O_TRUNC 若文件存在并且以可写的方式打开时,此标志会将文件长度清为0,而原来存于该文件的资料也会消失 重定向>
O_CREAT 若路径中的文件不存在则自动建立该文件
O_EXCL 如果与O_CREAT同时设置,此指令会去检查文件是否存在,文件若不存在则建立该文件,否则将导致打开文件错误。此外,若O_CREAT与O_EXCL同时设置,并且将要打开的文件为符号连接,则将导致打开文件失败
mode 如果需要open创建文件,mode是给这个创建的文件一个默认权限的。
- 接口2>ssize_t write (int fd, const void * buf, size_t count);
fd:文件描述符
buf:要写入的数据
count:写入数据的长度
返回值:实际写入的长度 失败,-1
- 接口3>ssize_t read(int fd, void * buf, size_t count);
返回值 实际读取的字节,失败:-1
fd:文件描述符
buf:要读取的数据存入的位置
count:读取数据的长度
- 接口4>off_t lseek(int fd, off_t offset, int whence);
fd 表示要操作的文件描述符
offset 是相对于whence(基准)的偏移量
whence 可以是SEEK_SET(文件指针开始),SEEK_CUR(文件指针当前位置) ,SEEK_END为文件指针尾
返回值: 文件读写指针距文件开头的字节大小,出错,返回-1
当我们打开一个空文件时,默认情况下文件指针指向文件流的开始。write和read函数本身自带移动文件指针的功能,当我write、read n个字节后,文件指针会从文件流的开始自动向后移动n位。所以当我们用lseek显式的将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的。
所以我们可以用这个接口来扩展文件大小、获取文件大小、用lseek构建空洞文件(空洞文件就是这个文件中有一段是空的。我们打开一个文件后,用lseek往后跳过一段,再write写入一段,就会构成一个空洞文件。)
-
《二》相关概念
1>文件描述符
进程对文件的管理:描述 + 组织
描述:数组的下标
组织:结构体数组;描述符就是结构体数组的下标,通过下标找到文件描述信息,进行对文件的操作
分配规则:遵循最小未使用的下标。
2>文件流流指针 FILE*
文件流指针与文件描述符的关系
文件流指针这个结构体中封装了文件描述符作为成员变量
文件流指针包含了缓冲区。普通文件不具备刷新缓冲区的功能,只有标准输出文件才有这个功能
3>文件重定向
改变描述符下标所对应的文件描述信息
函数:
int dup2(int oldfd, int newfd);
成功返回新的文件描述符,失败则返回-1
newfd来指定新描述符数值,若newfd指向的文件已经被打开,会先将其关闭。若newfd等于oldfd,就不关闭newfd,newfd和oldfd共同指向一份文件
提一下dup(oldfd):这个等于创建一个文件描述符也指向这个文件
操作符:
> 清空文件原有的内容,将新数据写入
>> 将新数据追加写入到文件中
对之前编写的自主shell进行修改,使其支持输入/输出/追加重定向
//C语言
1 //1.获取标准输入
2 //2.对输入字符串进行解析(获取程序名称 + 参数)
3 //3.创建子进程
4 // 程序替换
5 //4.进程等待
6
7 #include<stdio.h>
8 #include<unistd.h>
9 #include<stdlib.h>
10 #include<fcntl.h>
11 #include<string.h>
12 char buff[1024] = {0};//输入缓冲
13 char* argv[1024];
14 int argc = 0;
15 void menu(char* buff)//输入菜单
16 {
17 printf("[top-down@localhost]$");
18 fflush(stdout);
19 //判断进行回车判定,防止第一个字符就是回车
20 if(scanf("%[^\n]*c",buff) != 1)//^\n正则表达式,遇到\n截至,*c(s)表示抛弃剩下的字符(字符串)
21 {
22 getchar();
23 }
24 printf("%s\n",buff);
25 }
26 void explain(char* buff)//对输入字符串进行解析
27 {
28 char* ptr = buff;
29 while(*ptr != '\0')//不到结尾,一直循环
30 {
31 //当前位置非空白字符
32 if(!isspace(*ptr))//isspace() 检测空白字符,制表符、换行、回车、空格等
33 {
34 argv[argc++] = ptr;//保存字符串地址
35 while(!isspace(*ptr) && *ptr != '\0')
36 {
37 ++ptr;
38 }
39 }
40 else
41 {
42 *ptr = '\0';
43 ++ptr;
44 }
45 }
46 argv[argc] = NULL;
47 int i;
48 for(i = 0; i < argc; ++i)
49 {
50 printf("[%s]",argv[i]);
51 }
52 printf("\n");
53 }
54 void create()
55 {
56 pid_t pid = fork();
57 if(pid < 0)
58 {
59 exit(-1);
60 }
61 else if(pid == 0)
62 {
63 int i = 0;
64 int flag = 0;
65 char* str = ">>";
66 for(i = 0; argv[i] != NULL; ++i)
67 {
68 if(strcmp(str,argv[i]) == 0)
69 {
70 flag = 1;
71 break;
72 }
73 }
74 argv[i] = NULL;
75 int newfd;
76 if(flag == 1)
77 {
78 if(argv[i+1] == NULL)
79 {
80 printf("ERROR!\n");
81 exit(-1);
82 }
83 else
84 {
85 newfd = open(argv[i+1],O_RDWR|O_CREAT,0664);
86 dup2(newfd,1);
87 }
88 }
89 execvp(argv[0], argv);
90 close(newfd);
91 //防止程序替换失败,直接退出
92 exit(0);
93 }
94 wait(NULL);
95 }
96 int main()
97 {
98 menu(buff);
99 explain(buff);
100 create();
101 return 0;
102 }
-
《三》文件系统
a、五大部分:超级块,inode结点位图,数据位图,inode,data
普通文件和目录:目录也是文件,数据存储的是目录项(文件名和inode节点号)
例如: cat file:通过文件名在目录项中获取文件inode结点,通过inode结点再到存储位置,读取文件
b、软连接和硬链接:ln
软链接:类似快捷方式,存储的是文件的路径。软连接文件和源文件不是同一个文件,有不同的inode号
硬链接:类似引用,文件的别名。硬链接文件具有相同的inode结点号
根据上图更容易理解以下的特性:
删除源文件,软连接失效,但是硬链接不影响,只是连接数-1(当一个连接数为0的时候才是真正删除一个文件,否则只是删除目录项)
软连接可以针对目录创建,硬链接不可以
软连接可以跨分区创建,硬链接不可以
c、 相关命令:
fd:查看分区挂载情况
du:文件占磁盘空间大小
-
《四》动态库和静态库
动态链接:生成链接
静态链接:直接拷贝代码
I、库 :保存实现函数代码的文件
II、生成库:需要将所有目标文件集合到一起,生成一个打包后的代码库
III、 生成动态库:
gcc -fPIC -c b.c -o b.o fPIC:产生位置无关代码
gcc --share -o xxx.so share:生成共享库
动态库命名:以lib为前缀,以.so为后缀,中间是库名称
IV、生成静态库:
ar(静态库链接器) cr(create创建 raplace模块替换)
ar -cr xxx.a b.o
静态库命名:以lib为前缀,以 .a 为后缀,中间是库名称
V、静态库/动态库的链接:
gcc -o -a -lmy_b
生成可执行程序时,系统需要的所有库都在指定路径(/lib64)下,系统根据名称进行寻找。
如果不想自己的库将库放在/lib64,可以指定库查找路径(只针对静态库)。链接的时候,优先是动态库,所以在/lib64目录下不能有相同名称的动态库。
-L 指定路径
-l 指定链接的库名称
动态链接生成的可执行程序,需要动态库的存在,并且存在于指定路劲下,因此最好直接拷贝到/lib84
例如:实现一个动态库
我们先创建我们的函数:
接下来我们需要把写的文件封装成动态库
动态库只能在默认的lib64下,所以我们复制下
接下来我们写一个test.c
现在我们进行链接
a 和 b都是整型的。所以商是 0 。
ldd 查看链接库