七、Linux文件系统编程

七、Linux文件系统编程


一、传入参数、传出参数和传入传出参数

传入参数
1.指针作为函数参数;
2.通常有 const 关键字修饰;
3.指针指向有向区域,在函数内部做读操作;
比如:char *strcpy(char *dest, const char *src); 中的 const char *src

传出参数
1.指针作为函数参数;
2.在函数调用之前,指针指向的空间可以无意义,但必须有效;
3.在函数内部做写操作;
4.函数调用结束后,充当函数返回值;
比如:char *strcpy(char *dest, const char *src); 中的 char *dest

传入传出参数
1.指针作为函数参数;
2.在函数调用之前,指针指向的空间有实际意义;
3.在函数内部,先做读操作,后做写操作;
4.函数调用结束后,充当函数返回值;


二、dentry和inode

dentryinode 是描述文件的最主要的两个属性

1. 首先 dentry 存放的是文件名和相应的 inode 编号,而 inode 是一个结构体,存放着文件的属性,如:权限、大小、盘符、用户组等信息

2. 文件的内容最终保存的位置是电脑的磁盘。通过文件名可以访问到文件的 dentry,在 dentry 上可以得到文件对应的 inode 编号,通过 inode 可以访问到文件的盘符信息,进而可以在电脑磁盘上访问到文件

3. 实际上,硬链接与原文件不同的地方在于 dentry,即不同的文件名,而 inode 编号是相同的,因此,通过硬链接也可以访问到原文件的 inode 信息,硬链接因此与原文件紧密关联

如图所示:
在这里插入图片描述
为什么目录项 dentry 要游离于 inode 之外,画蛇添足般的将文件名单独存储呢?

其目的是为了实现文件共享,Linux允许多个目录项共享一个 inode,即共享盘块(data),不同文件名,在人类眼中将它理解成两个文件,但是在内核眼里是同一个文件


三、stat和lstat函数
1.函数原型

包含头文件

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *buf);

获取文件属性结构体,从 inode 结构体中获取

const char *pathname 文件路径
struct stat *buf inode 结构体指针(传出参数)
返回值 成功时返回 0,失败返回 -1errno


int lstat(const char *pathname, struct stat *buf);

stat() 函数用法一样,它们的区别在于,stat() 可以穿透软链接,而 lstat() 不会穿透软连接

注意:穿透软连接的意思是将软链接视为原文件

struct stat *buf 结构体成员

struct stat {
	dev_t     st_dev;         /* 包含文件的设备的id */
	ino_t     st_ino;         /* inode 编号 */
	mode_t    st_mode;        /* 低9位为权限位,高4位为文件类型位 */
	nlink_t   st_nlink;       /* 硬链接数 */
	uid_t     st_uid;         /* 用户 ID */
	gid_t     st_gid;         /* 组 ID */
	dev_t     st_rdev;        /* device ID (if special file) */
	off_t     st_size;        /* 文件大小 */
	blksize_t st_blksize;     /* blocksize for filesystem I/O */
	blkcnt_t  st_blocks;      /* 扇区数量(一个扇区 512 bit) */
	
	/* Since Linux 2.6, the kernel supports nanosecond
	precision for the following timestamp fields.
	For the details before Linux 2.6, see NOTES. */
	
	struct timespec st_atim;  /* 最后访问时间 */
	struct timespec st_mtim;  /* 属性最后修改时间 */
	struct timespec st_ctim;  /* 内容最后修改时间 */
	
	#define st_atime st_atim.tv_sec      /* Backward compatibility */
	#define st_mtime st_mtim.tv_sec
	#define st_ctime st_ctim.tv_sec
};

例如:写一个指令,使其能通过 指令 <文件名>,来显示文件的大小

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

int main(int argc, char *argv[])
{
    struct stat sbuf;

    int ret = stat(argv[1], &sbuf);
    if(ret == -1)
    {
        perror("stat error");
        exit(1);
    }

    printf("file_size:%ld\n", sbuf.st_size);

    return 0;
}

在这里插入图片描述

2.宏函数

在通过 stat() 函数取得文件属性结构体后,还可以通过宏函数来对文件的一些属性进行判断:参数为 st_mode (取高4位)

在这里插入图片描述
是就返回 1,否则返回 0

例如

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

int main(int argc, char *argv[])
{
    struct stat sbuf;

    int ret = stat(argv[1], &sbuf);
    if(ret == -1)
    {
        perror("stat error");
        exit(1);
    }

    if(S_ISREG(sbuf.st_mode))
    {
    	printf("It's a regular file\n");
    }
    else if(S_ISDIR(sbuf.st_mode))
    {
    	printf("It's a directory\n");
    }else if(S_ISCHR(sbuf.st_mode))
    {
    	printf("It's a character device\n");
    }else if(S_ISBLK(sbuf.st_mode))
    {
    	/* ... */
    }
    
    /* ... */

    return 0;
}

注意:由于 stat() 可以穿透软链接,其获取的 sbuf.st_mode 在使用 S_ISLNK() 询问是否为软链接时,都不会是软链接,而是原文件类型,而 lstat() 获取的 sbuf.st_mode 就会回答是软链接
在这里插入图片描述
使用 stat() 的结果:
在这里插入图片描述
使用 lstat() 的结果:
在这里插入图片描述


四、link、unlink和symlink函数

首先明确
在这里插入图片描述

1.函数原型

包含头文件

#include <unistd.h>

int link(const char *oldpath, const char *newpath);

可以为已经存在的文件创建目录项(硬链接)

const char *oldpath 原文件路径
const char *newpath 硬链接路径
返回值 成功时返回 0,失败返回 -1errno


int unlink(const char *pathname);

删除一个文件的目录项(硬链接),可以删除原文件路径

const char *pathname 文件路径
返回值 成功时返回 0,失败返回 -1errno

删除文件实际上就是删除文件的最后一个硬链接,即没有 dentry 与之对应,这样就可以使系统无法访问文件在磁盘中的位置,但文件在磁盘上的内容依旧存在;要等到所有打开该文件的进程关闭,该文件才会被操作系统择机释放,即等待其他内容覆盖之

终端命令 mv 实际上只是改变了文件的目录项

例如:模拟终端指令 mv

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    int ret = link(argv[1], argv[2]);
    if(ret == -1)
    {
        perror("link error");
        exit(1);
    }

    ret = unlink(argv[1]);
    if(ret == -1)
    {
        perror("unlink error");
        exit(1);
    }

    return 0;
}
2.隐式回收

当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放,Linux系统的这一特性称之为隐式回收系统资源

因此,即使文件的最后一个硬链接被 unlink 掉,只要还有打开了该文件的进程正在运行,它的系统资源就仍然存在,仍然可以对该文件进行操作

3.symlink函数

包含头文件

#include <unistd.h>

int symlink(const char *target, const char *linkpath);

创建一个符号链接(软连接)

const char *target 原文件路径
const char *linkpath 软连接路径
返回值 成功时返回 0,失败返回 -1errno

4.与链接相关的其他函数

包含头文件

#include <unistd.h>

ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

读取符号链接(软链接)所链接的文件的内容

const char *pathname 软链接路径
char *buf 存数据的缓冲区,其大小决定每次读取数据的字节大小
size_t bufsiz 缓冲区的字节大小,相当于每次读取数据的字节大小
返回值 成功则返回读到的字节数,当读到文件末尾时,读到的字节数为 0,会返回 0,失败时返回 -1errno

包含头文件

#include <stdio.h>

int rename(const char *oldpath, const char *newpath);

重命名一个文件

const char *oldpath 原文件路径
const char *newpath 新文件路径
返回值 成功时返回 0,失败返回 -1errno


五、其他函数
1.chmod函数

包含头文件

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);

修改文件的访问权限

const char *pathname 文件路径
mode_t mode 修改后的权限,特殊权限位+三位八进制,受 umask 影响
返回值 成功返回 0,失败返回 -1errno

2.truncate函数

包含头文件

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *path, off_t length);

截断文件长度成指定长度,常用来拓展文件大小

const char *path 文件路径
off_t length 截断后的大小
返回值 成功返回 0,失败返回 -1errno

3.access函数

包含头文件

#include <unistd.h>

int access(const char *pathname, int mode);

测试指定文件是否存在/拥有某种权限

const char *pathname 文件路径
int mode 需要验证的权限
返回值 成功/具备该权限时返回 0,失败/不具备该权限返回 -1errno

int mode作用
R_OK是否具有读权限
W_OK是否具有写权限
X_OK是否具有可执行权限
F_OK文件是否存在

六、目录及其相关函数
1.文件和目录的权限差异
权限文件目录
r可读(catmorelessvi)目录可被浏览(lesstree)
w可写/修改创建、删除、修改文件(mvtouchmkdir)
x可以运行产生一个进程可以被打开、进入(cd)

目录文件也是文件,其文件内容是该目录下所有子文件的目录项 dentry

2.getcwd和chdir函数

包含头文件

#include <unistd.h>

char *getcwd(char *buf, size_t size);

获取进程当前工作目录,man 手册第3卷——标库函数

char *buf 传出参数,存放工作目录(字符串)
size_t size 传出参数 buf 的大小
返回值 成功时返回工作目录(字符串),失败返回 NULLerrno


int chdir(const char *path);

改变当前进程的工作目录

const char *path 改变后的工作目录
返回值 成功返回 0,失败返回 -1errno

3.opendir/closedir函数

包含头文件

#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);

根据传入的目录名打开一个目录/库函数

const char *name 目录/库函数路径
返回值 DIR * 类似于 FILE *,成功返回该目录结构体指针,失败返回 NULLerrno


int closedir(DIR *dirp);

关闭打开的目录/库函数
DIR *dirp 要关闭的目录的 DIR
返回值 成功返回 0,失败返回 -1errno

const char *name 可以是绝对路径,可以是相对路径,可以通过 char *path = getcwd() 获取当前工作目录

可以通过 man 手册查询,在第 3

man 3 opendir
man 3 closedir
4.readdir函数

包含头文件

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

读取目录/库函数,根据目录偏移指针位置来读

DIR *dirp 要读取的目录的 DIR
返回值 成功时返回目录项结构体指针,根据目录偏移指针位置来返回,读到结尾(读完最后一个文件)时返回 NULL,失败返回 NULLerrno

目录项结构体 struct dirent 成员图
在这里插入图片描述
例如:模拟终端指令 ls

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>

int main(int argc, char *argv[])
{
    struct dirent *sdp;

    DIR *dp = opendir(argv[1]);
    if(dp == NULL)
    {
        perror("opendir error");
        exit(1);
    }

    while((sdp = readdir(dp)) != NULL)
    {
        printf("%s\t", sdp->d_name);
    }
    printf("\n");

    int ret = closedir(dp);
    if(ret == -1)
    {
        perror("closedir error");
        exit(1);
    }

    return 0;
}
5.目录偏移指针

目录偏移指针的位置也可以被指定,相关函数:rewinddir()、telldir()、seekdir()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值