七、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()

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模拟Linux文件系统。在任一OS下,建立一个大文件,把它假象成一张盘,在其中实现一个简单的 模拟Linux文件系统 在现有机器硬盘上开辟20M的硬盘空间,作为设定的硬盘空间。 2. 编写一管理程序对此空间进行管理,以模拟Linux文件系统,具体要求如下: (1) 要求盘块大小1k 正规文件 (2) i 结点文件类型 目录文件 (共1byte) 块设备 管道文件 物理地址(索引表) 共有13个表项,每表项2byte 文件长度 4byte 。联结计数 1byte (3)0号块 超级块 栈长度50 空闲盘块的管理:成组链接 ( UNIX) 位示图法 (Linux) (4)每建一个目录,分配4个物理块 文件名 14byte (5)目录项信息 i 结点号 2byte (6)结构: 0#: 超级块 1#-20#号为 i 结点区 20#-30#号为根目录区 3. 该管理程序的功能要求如下: (1) 能够显示整个系统信息,源文件可以进行读写保护。目录名和文件名支持全路径名和相对路径名,路径名各分量间用“/”隔开。 (2) 改变目录:改变当前工作目录,目录不存在时给出出错信息。 (3) 显示目录:显示指定目录下或当前目录下的信息,包括文件名、物理地址、保护码、文件长度、子目录等(带/s参数的dir命令,显示所有子目录)。 (4) 创建目录:在指定路径或当前路径下创建指定目录。重名时给出错信息。 (5) 删除目录:删除指定目录下所有文件和子目录。要删目录不空时,要给出提示是否要删除。 (6) 建立文件(需给出文件名,文件长度)。 (7) 打开文件(显示文件所占的盘块)。 (8) 删除文件:删除指定文件,不存在时给出出错信息。 4. 程序的总体流程为: (1) 初始化文件目录; (2) 输出提示符,等待接受命令,分析键入的命令; (3) 对合法的命令,执行相应的处理程序,否则输出错误信息,继续等待新命令,直到键入EXIT退出为止。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值