硬链接,软链接,link,rename,symlink,opendir和readdir

什么是硬链接

struct stat {
               nlink_t   st_nlink;       /* Number of hard links
            };

stat结构体就有一个成员变量----硬链接数

  • 使用ln命令就可以创建硬链接

  • 创建硬链接,就是再为文件创建一个名字

每创建一个硬链接,文件就多一个文件名,硬件链接数+1

  • 多个文件名指向了同一个文件,操作文件时,使用任何一个名字都可以

创建硬链接后所得到的多个文件名,指向的同一个inode节点,只有inode节点代表了文件的真实存在,inode节点只有一个,因此多个文件名指向的是同一个文件,不管使用的是哪一个文件名,都能操作这个文件。

硬链接数
  • 记录了有多少个文件名指向了inode节点,通过创建硬链接,每增加一个文件名,就多一个硬链接数
  • 同理,每删除一个硬链接,也就是删除一个文件名,就少一个硬链接数

删除文件时,只是将文件的inode节点空间释放了,如果这个文件有数据的话,那么这个文件的数据仍然还在,在这种情况下,只要将文件的inode节点空间恢复,即可还原该文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7fe3Diap-1667544982020)(/home/guojiawei/.config/Typora/typora-user-images/image-20221104144320671.png)]

如图,新创建的目录的硬链接数是2.

因为新创建的目录,一开始就有两个名字指向了目录的inode节点,分别是目录的本名text和.

  • 在该目录下,每多创建一个目录,当前目录就会多一个硬链数

Linux不允许用户自己给目录创建硬链接,只能由Linux系统自己给目录创建硬链接

用户只能给目录以外的,其它类型的文件创建硬链接

目录

目录是特殊的文件,本质上是一个表格,用于存储文件名和inode节点空间

可以在目录中创建多个链接指向文件,这些链接指向相同的inode节点空间

link函数

#include <unistd.h>
//功能:为非目录文件建立一个新的硬连接(ln命令就是调用这个系统函数来实现的)
//返回值:调用成功返回0,失败返回-1,errno被设置
int link(const char *oldpath, const char *newpath);
 link("./mm.txt","./mm1.txt");

unlink函数

#include <unistd.h>
//功能:删除一个硬连接,其实就是删除一个名字。	
//返回值:调用成功返回0,失败返回-1,errno被设置
//参数:pathname:要删除路径名
int unlink(const char *pathname);

只能用于删除非目录文件的硬链接,不能删除目录的硬链接,Linux系统不允许用户修改目录的硬件链接

使用unlink创建临时文件

临时文件:

  • 只在程序运行过程中有效,程序运行结束后就自动删除

  • 使用unlink就可以实现一个临时文件

open创建一个文件后(新文件的硬链接数都是1),然后立即调用unlink将文件硬链接数减为0,将其删除。

虽然文件的硬链接数变成了0,但是在进程没有结束之前,这个文件仍然可以被使用,直到进程结束后,文件才被删除

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
    int fd=open("mm.txt",O_RDWR|O_CREAT,0664);
    if(fd==-1){
        perror("open fail!");
        exit(-1);
    }
    unlink("./mm.txt");
    write(fd,"hello,world!\n",12);
    lseek(fd,0,SEEK_SET);
    char buf[30]={0};
    read(fd,buf,sizeof(buf));
    printf("buf=%s\n",buf);
    return 0;
}

remove函数

#include <stdio.h>
//功能:可以用于删除任何文件(既可以删除目录文件,也可以删除非目录文件)
//删除非目录文件时,功能与unlink一样。
//返回值:调用成功返回0,失败返回-1,errno被设置
int remove(const char *pathname);
int main(){
    remove("./aaa");//删除目录aaa
    return 0;
}

rename函数

mv命令

  • 改名
  • 移动
  • 移动+改名

mv ./mm.txt …/mm_aa.txt

#include <stdio.h>
//功能:修改文件路径名,将旧的路径名oldpath,改为新的路径名newpath
//返回值:调用成功返回0,失败返回-1,errno被设置
int rename(const char *oldpath, const char *newpath);
rename("./mm_aa.txt","./aaa/mm.txt");//使用

如果文件移动起始位置和目标位置,在同一个分区里面的话

移动文件时,不会移动文件的数据;

只是把文件的基本信息(名字、inode编号),从这个目录记录到另一个目录下。

什么是符号链接

符号链接文件也被称为软链接文件

使用ln -s就可以创建符号链接文件

  • 符号链接文件就是一个快捷图标,它指向了另一个文件

和windows一样,原文件被删除了,快捷指向就没意义了


软链接和硬链接的对比

  • 创建硬连接

    同一个文件有多个不同的名字,它们指向是同一个inode节点

  • 创建符号链接文件

    符号链接文件与它所指向的文件,是两个完全不同的独立的文件,拥有自己独立的inode节点。

    符号链接文件的数据就是指向文件的文件名,文件大小就是名字的字符个数

不能给目录创建硬链接,但是可以给目录创建符号链接;只要你有需要,可以给任何文件创建符号链接文件。而且可以给符号链接文件,创建硬链接

s_link是link目录的软链接,p_s_link是s_link的硬链接

symlink

#include <unistd.h>
//功能:为oldpath,创建符号连接文件newpath
//函数返回值:调用成功返回0,失败返回-1,errno被设置
int symlink(const char *target, const char *linkpath);

使用ln创建硬链接时,调用的是link函数; 使用ln -s创建符号链接时,调用的是symlink

readlink

#include <unistd.h>
//功能:读符号链接文件的数据(指向文件的名字)
/*
    1)const char *path:符号连接文件的路径名。
	2)char *buf:存放名字缓存的地址。
	3)size_t bufsiz:缓存的大小
*/
//返回值:调用成功,返回读到的字节数,失败返回-1,errno被设置
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

int main(){
    symlink("./link.c","slink");
    char buf[30]={0};
    readlink("./slink",buf,sizeof(buf));
    printf("buf=%s\n",buf);
    return 0;
}

stat半成品

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void print_error(char *str){
	perror(str);
	exit(-1);
}

int main(int argc, char **argv){
	int ret = 0;
	int i = 0;
	struct stat sta = {0};
	if(argc != 2){
		printf("./a.out fileName\n\n");
		exit(-1);
	}

	/* 获取文件属性 */
	ret = lstat(argv[1], &sta);
	if(-1 == ret) print_error("stat fail");
	
	/* 打印文件类型 */
	char file_type = '0';	
	if(S_ISLNK(sta.st_mode)) file_type = 'l'; 
	else if(S_ISREG(sta.st_mode)) file_type = '-';
	else if(S_ISDIR(sta.st_mode)) file_type = 'd';
	else if(S_ISCHR(sta.st_mode)) file_type = 'c';
	else if(S_ISBLK(sta.st_mode)) file_type = 'b';
	else if(S_ISFIFO(sta.st_mode)) file_type = 'p';
	else if(S_ISSOCK(sta.st_mode)) file_type = 's';
	printf("%c", file_type);

	/* 打印文件权限 */	
	char buf[10] = {0};
	char tmp_buf[] = "rwxrwxrwx";
	for(i=0; i<9; i++)
	{
		if(sta.st_mode & (1<<(8-i))) buf[i] = tmp_buf[i];
		else buf[i] = '-';
	}
	printf("%s", buf);
	
	/* 打印文件属性 */
	printf(" %lu %u %d %ld %ld %s", sta.st_nlink, \
		sta.st_uid, sta.st_gid, sta.st_size, sta.st_atime, argv[1]);
    //打印出链接文件的指向
	if(file_type=='l'){
		char mm[30]={0};
		readlink(argv[1],mm,sizeof(mm));
		printf("->%s\n",mm);
	}
	else printf("\n");

	return 0;
}

符号跟随函数 与 符号不跟随函数

(1) 符号跟随函数

调用某个函数操作文件,当指定的路径名是符号链接文件时:

如果函数最后操作的是符号链接文件所指向的文件,而不是“符号链接文件”本身,

这个函数就是符号跟随函数,因为它跟到符号链接文件所指向的背后去了。

  • 比如stat、open就是符号跟随函数,因为这些操作的都是符号链接文件所指向的文件

(2)符号不跟随函数

当路径名是符号链接文件时,函数操作的就是符号链接文件本身,不会跟随

比如lstat就是符号不跟随函数,因为获取文件属性时,如果操作的是符号链接文件的话,那么获取的是符号链接文本身的属性。

  • 凡是需要指定“文件路径名”函数,只要函数名字是l打头的,比如像lstat,基本都是符号不跟随函数。
  • lseek不需要指定路径名,而是通过文件描述符(fd)操作的,不存在符号跟随与不跟随的问题。

getcwd

#include <unistd.h>
//这是一个库函数,执行pwd命令就是调用这个函数实现的
//功能:获取进程的当前工作目录
/*参数
	1)buf:存放获取到的当前路径的缓存
	2)size:缓存的大小
*/
char *getcwd(char *buf, size_t size);
int main(){
    char buf[30]={0};
    char *p=getcwd(buf,100);
    printf("pwd=%s\n",buf);
    printf("p=%p,buf=%p\n",p,buf);
    return 0;
}
/*执行结果:
pwd=/home/guojiawei/Desktop/link
p=0x7ffe8d093ac0,buf=0x7ffe8d093ac0
getcwd的返回值是存放路径的地址
*/

chdir

change chdir

#include <unistd.h>
//功能:切换进程当前工作目录到path。
int chdir(const char *path);
int main()
{
    char buf[30] = {0};
    char *p = getcwd(buf, 100);
    printf("pwd=%s\n", buf);
    chdir("../");
    getcwd(buf, 100);
    printf("pwd_cur=%s\n", buf);
    return 0;
}
/*执行结果:
pwd=/home/guojiawei/Desktop/link
pwd_cur=/home/guojiawei/Desktop
*/

cd命令就是调用chdir的命令

mkdir

#include <sys/stat.h>
#include <sys/types.h>
//功能:创建新目录。
/*参数:
  1)pathname:需创建目录的路径名
  2)mode:指定目录的原始权限,一般给0775
*/
int mkdir(const char *pathname, mode_t mode);

给目录指定原始权限时,一定要有x权限,否者无法进入这个目录

rmdir

rm命令删除目录时,调用的都是rmdir这个函数

  • rmdir命令:只能删除空目录,rmdir命令用的很少
  • rm:不管目录空不空,都能删除,rm用的最多
	                 rm
					 |
				   remove
				  |     |
				  |     |
				 /       \
				/         \
			unlink 	     rmdir
#include <unistd.h>
//函数功能:删除路径名为pathname的这个目录
int rmdir(const char *pathname);

删除时,Linux系统会调用相关函数,将目录硬链数全部减位0,然后目录就被删除了

如果目录不为空,必须递归调用rmdir函数,实现递归删除

opendir和readdir

  • opendir:打开目录,以便调用readdir读取目录项
  • readdir:读取目录里面的目录项

目录项:
目录里面的数据,其实就是一条一条的目录项,每个目录项就是一个文件的基本信息

文件基本信息:文件名inode节点号

不能使用open函数打开目录,只能使用opendir打开

#include <sys/types.h>
#include <dirent.h>
//功能:打开目录
//参数:name---需打开目录的路径名
/*返回值:
 调用成功:返回一个DIR *的指针,这个指针指向了被打开的目录,readdir通过这个指针就可以读取目录的目录项。
 调用失败:返回NULL,errno被设置。
*/
DIR *opendir(const char *name);
#include <dirent.h>
//功能:读取目录里的目录项-----每调用一次,就读取出一条目录项。
//参数dirp:opendir打开目录时,得到的指针。
//返回值:调用成功,返回指针指向struct dirent结构体的指针
/*
返回NULL的话有如下两种情况:
						1)读到目录的末尾时,返回NULL。
						2)函数调用失败时,也返回NULL,不过errno被设置。	
*/
struct dirent *readdir(DIR *dirp);
  • 结构体 struct dirent

    这个结构体就是用来存放一条目录项的:

    调用readdir读取到目录项后,会自动开辟一个struct dirent变量来存放目录项,然后将变量的指针返回;

    应用程序通过这个指针,就可以访问结构体中的目录项信息(文件基本信息)。

    struct dirent {
                   ino_t          d_ino;       /* Inode number */
                   off_t          d_off;       /* Not an offset; see below */
                   unsigned short d_reclen;    /* Length of this record */
                   unsigned char  d_type;      /* Type of file; not supported
                                                  by all filesystem types */
                   char           d_name[256]; /* Null-terminated filename */
                 };
    //最重要的就是:inode编号和文件名
    

    怎么判断函数是否调用失败了呢?

    • 如果ernno==0,表示没有设置错误号,返回NULL是因为读到了文件的末尾。
    • 如果errno!=0,表示是因为函数调用出错而返回的NULL。
#include <stdio.h>
#include <stdlib.h>
//opendir头文件
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int main(){
    DIR *pot=opendir(".");
    if(pot==NULL){
        perror("opendir fails!");
        exit(-1);
    }
    while(1){//循环读
    struct dirent *dir_p=readdir(pot);
    if(dir_p==NULL && errno!=0){
         perror("readdir fails\n");
    }
    if(dir_p==NULL && errno==0) break;//读到末尾
    printf("inode=%lu,fname=%s\n",dir_p->d_ino,dir_p->d_name);
    }
    return 0;
}

chmod 和 fchmod

#include <sys/stat.h>
//功能:修改文件权限
//chmod:使用路径名操作
//fchmod:使用文件描述符操作
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值