文章目录
- Linux文件编程
- 文件操作的基本元素(文件在磁盘上的操作)
- 软链接和硬链接
- 原理
- 文件系统范围
- 创建方式
- 示例:在终端使用指令创建软硬链接
- 软链接
- 硬链接
- 删除源文件影响
- 软链接和硬链接实现的具体原理
- link函数
- unlink函数
- 代码示例:使用`link`创建硬链接
- 代码示例:使用`unlink`删除硬链接
- remove函数
- rename函数
- 代码示例--使用`remove`解除对文件或者目录的连接
- 代码示例--使用`rename`函数给文件重命名
- symlink函数
- readlink函数
- 代码示例--使用`symlink`和`readlink`函数创建软链接和查找源文件
Linux文件编程
文件操作的基本元素(文件在磁盘上的操作)
文件操作最基本的元素是:目录结构、索引节点和文件数据的本身
- 目录结构(目录项 用来存放文件路径和文件的索引节点)
- 索引节点(i节点 用来存放数据块的块号)
- 文件的数据
在磁盘上操作文件的流程
在Linux文件系统结构中有一个目录项,这个目录项存放了文件的路径和文件的i节点。当我们使用指令去操作一个文件的时候,首先会获取到文件的路径,然后根据目录项来获取到i节点,i节点中存放的文件的数据块号,根据这个数据块号就能访问到对应的数据区。同时在Linux磁盘中的i节点和内核中的i节点是同步的,所以对文件的读写操作可以通过内核中的i节点同步磁盘中的i节点。
软链接和硬链接
原理
- 软连接:软连接实际上是一个独立的特殊类型的文件,它存储的是目标文件或目录的路径。当你访问软链接时,系统会跟踪这个路径并解析到真正的目标文件。因此,软链接更像是一个指向目标文件的指针或快捷方式。
- 硬链接:硬链接实际上是同一文件系统中同一个文件的多个“入口”。每个硬链接指向的是同一个文件的inode(索引节点),所有硬链接共享相同的inode和数据块。这意味着,无论你通过哪个硬链接访问文件,实际上看到的都是同一份数据。
文件系统范围
- 软链接:可跨越不同的文件系统进行创建,而且能够对文件、目录进行创建软链接,同时也能够对不存在的文件创建软链接,只不过由于没有源文件是一个无效的符号链接文件;
- 硬链接:不能跨系统操作,只能对文件创建硬链接,且必须是在同一个分区。
创建方式
-
软链接:在Linux中使用
ln -s
来创建软链接; -
硬链接:使用
ln
创建,无需指定参数;
示例:在终端使用指令创建软硬链接
软链接
- 给普通文件创建软链接:
ln -s 源文件或目录 目标文件或目录
使用ls -l
指令查看创建的文件可以发现,它的文件类型是一个符号链接文件,而且它所引用的源文件是temp.c
- 给目录创建软链接
首先在当前目录下创建一个目录,然后在此目录下创建一个文件进行编辑,然后给此目录创建软链接。可以发现通过软链接文件ld_test
可以访问到test
目录下的文件并进行修改。所以软链接文件是可以进行跨分区、跨系统进行操作的,并且使用方法和源文件或者目录没有区别,就像是创建了一个快捷方式。
硬链接
-
给普通文件创建硬链接:
上边使用
ln
指令给temp.c
创建了一个硬链接,然后使用ls -l
指令查看硬链接的文件类型,可以发现它是一个普通类型的文件。而且这里的2
代表硬链接数,如果只有源文件的话,它的硬链接数是1,每创建一个硬连接,硬连接数就会加1。并且通过硬链接文件也能够访问到源文件里边的内容。 -
给目录创建硬链接:
通过尝试发现并不能给目录创建硬链接,之前在网上看到过说超级用户可以对目录创建硬链接,但实际测试好像并不可以。(具体原因下边会详细介绍)
删除源文件影响
-
软链接:由于源文件被删除,所以软链接文件就失去了它的指向。因此当尝试通过软链接去访问之前的文件或者目录的时候,就会显示没有此文件。
-
硬链接:即使是源文件被删除,但是还是能通过硬链接访问源文件里边的内容。
如下图:
首先,通过cat
指令来通过软链接和硬链接都能够访问到源文件里边的内容,当把源文件删除以后,通过ls -l
指令查看软链接,发现它的文件名变成红色了,这就是我们通常说的软连接失效了。然后通过ls -l
指令来查看硬链接发现它的硬链接数减1。通过cat
指令发现软链接不能进行访问了,但是硬链接仍旧可以访问。
软链接和硬链接实现的具体原理
-
硬链接
如图所示:当创建一个文件的时候,通过目录项可以得知文件的i节点,通过i节点就能找到它对应的数据块号,也就能访问到它所指向的数据区。当创建一个硬链接指向源文件的时候,它只创建一个目录项并且硬连接数做加1操作,并不会创建新的i节点和数据块,所以源文件和硬链接文件它们访问的是同一块数据区域,这也就解释了为什么当源文件删除以后硬链接还能够访问到源文件的内容,因为它所指向的这片数据区域没有被释放掉。所以当删除一个文件的时候,想要将内容清空,然后将这片数据区域释放,只有当它的硬连接数减为0的时候才说明它的源文件和硬链接文件全部删除了,也就意味这这片数据区域被释放掉了。不能对目录创建硬链接的原因是:如果想要删除这个目录,那么指向这个目录的硬链接的内容就不能同步了。这回破坏文件系统结构并且会造成数据的丢失。
-
软链接
如图所示:当创建一个软连接的时候,会创建一个新的目录项和i节点,而软链接文件的数据块所指向的内容是源文件的路径,所以当把源文件删除的时候,它数据块所引用的内容没有了,所以此时它找不到对应的文件或者目录。
示例–创建硬链接
link函数
#include <unistd.h>
int link(const char *path1, const char *path2);
//功能:创建一个指向现存文件链接(硬链接)
//参数1:源文件的路径
//参数2:目标文件的路径
//返回值:如果成功执行返回0,反之返回-1
unlink函数
#include <unistd.h>
int unlink(const char *path);
//功能:删除pathname指定的硬链接,并将由pathname所引用的文件链接计数减1
//参数:要删除的硬连接的路径
//返回值:成功执行返回0,反之返回-1
代码示例:使用link
创建硬链接
#include "header.h"
int main(int argc, char **argv)
{
if(argc < 3)
{
fprintf(stderr,"usage: %s [srcfile] [linkfile]\n",argv[0]);
exit(EXIT_FAILURE);
}
int i;
for(i=2;i<argc;i++)
{
if(link(argv[1],argv[i]) < 0)
{
perror("link error");
continue;
}
}
printf("link finished\n");
return 0;
}
经过编译执行后生成两个硬链接文件,而且它们的执行结果和源文件一样,并且硬连接数变为了3
代码示例:使用unlink
删除硬链接
#include "header.h"
int main(int argc, char **argv)
{
if(argc < 3)
{
fprintf(stderr,"usage: %s [srcfile] [linkfile]\n",argv[0]);
exit(EXIT_FAILURE);
}
int i;
for(i=1;i<argc;i++)
{
if(unlink(argv[i]) < 0)
{
perror("unlink error");
continue;
}
}
printf("unlink finished\n");
return 0;
}
通过编译执行,可以发现unlink
函数可以删除硬链接文件
remove函数
#include <stdio.h>
int remove(const char *path);
//功能:解除对一个文件或者目录的连接(对与文件,remove的功能与unlink相同;对于目录,remove的功能与rmdir相同)
//参数:要解除的文件的路径
//返回值:成功返回0,出错返回-1
rename函数
#include <stdio.h>
int rename(const char *old, const char *new);
//功能:文件或者目录重命名
//参数1:旧的文件名字或者目录名
//参数2:新的文件名或者路径名
//返回值:成功返回0,出错返回-1
代码示例–使用remove
解除对文件或者目录的连接
#include "header.h"
int main(int argc, char **argv)
{
if(argc < 2)
{
fprintf(stderr,"usage: %s [srcfile] [linkfile]\n",argv[0]);
exit(EXIT_FAILURE);
}
int i;
for(i=1;i<argc;i++)
{
if(remove(argv[i]) < 0)
{
perror("remove error");
continue;
}
}
printf("remove finished\n");
return 0;
}
remove
函数其实就是Linux中的指令rm
,所以它的用法也比较熟悉了
代码示例–使用rename
函数给文件重命名
#include "header.h"
int main(int argc, char **argv)
{
if(argc < 3)
{
fprintf(stderr,"usage: %s [srcfile] [renamefile]\n",argv[0]);
exit(EXIT_FAILURE);
}
int i;
for(i=2;i<argc;i++)
{
if(rename(argv[1],argv[i]) < 0)
{
perror("rename error");
continue;
}
}
printf("rename finished\n");
return 0;
}
这个函数的用法也比较简单,就像Linux指令中的mv
指令的重命名
示例–创建软链接
symlink函数
#include <unistd.h>
int symlink(const char *path1, const char *path2);
//功能:创建一个符号链接(软链接)
//参数1:源文件的路径
//参数2:要创建的那个文件的路径
//返回值:成功执行返回0,反之返回-1
readlink函数
#include <unistd.h>
ssize_t readlink(const char *restrict path, char *restrict buf,
size_t bufsize);
//功能:打开该链接本身,并读取到该链接所引用的源文件名字
//参数1:链接文件的路径
//参数2:用来存放读取到的内容的缓存
//参数3:缓存的大小
//返回值:成功返回独到的字节数,出错返回-1
代码示例–使用symlink
和readlink
函数创建软链接和查找源文件
#include "header.h"
int main(int argc, char **argv)
{
if(argc < 3)
{
fprintf(stderr,"usage: %s [srcfile] [linkfile]\n",argv[0]);
exit(EXIT_FAILURE);
}
int i;
char buffer[4096];
int fd;
ssize_t nbytes;
for(i=2;i<argc;i++)
{
if(symlink(argv[1],argv[i]) < 0)
{
perror("symlink error");
exit(EXIT_FAILURE);
}
}
printf("symlink finished\n");
fd = open(argv[2],O_RDONLY);
if(fd < 0)
{
perror("open file error");
exit(EXIT_FAILURE);
}
memset(buffer,'\0',sizeof(buffer));
//将符号链接文件里边的内容输出到屏幕上
if((nbytes = read(fd, buffer, sizeof(buffer))) < 0)
{
perror("read error");
exit(EXIT_FAILURE);
}
else
{
if(write(STDOUT_FILENO,buffer,nbytes) != nbytes)
{
perror("write error");
exit(EXIT_FAILURE);
}
}
memset(buffer,'\0',sizeof(buffer));
if((nbytes = readlink(argv[2], buffer, sizeof(buffer))) < 0)
{
perror("readlink error");
exit(EXIT_FAILURE);
}
else
{
fprintf(stdout,"readlink: \n%s\n",buffer);
}
return 0;
}
通过编译执行可以看到成功创建了软链接文件,而且也能够通过软链接文件访问到源文件的内容,也能够获取符号链接引用的文件。通过ls -l
指令验证也是一样的。