文件系统:目录与文件

This commit is contained in:
lzy
2024-04-30 20:41:19 +08:00
parent ba9f2e37a7
commit 5399da7488
11 changed files with 776 additions and 0 deletions

View File

@@ -1,3 +1,15 @@
# 目录
- [目录](#目录)
- [数据结构](#数据结构)
- [架构梳理](#架构梳理)
- [静态库与动态库](#静态库与动态库)
- [静态库](#静态库)
- [以链式双向链表的`lib2`为例。](#以链式双向链表的lib2为例)
- [动态库](#动态库)
- [还是以`lib2`为例。](#还是以lib2为例)
- [以链式存储栈为例,`libstack`依赖`libllist`。](#以链式存储栈为例libstack依赖libllist)
# 数据结构
## 架构梳理

View File

@@ -162,6 +162,7 @@ int setvbuf(FILE *stream, char *buf, int mode, size_t size);
*
* !!! 里面有 malloc 动作,未释放
* !!! 是方言可以自己封装一个mygetline和mygetline_free
* !!! 但是根据chatgpt好像直接 free(*lineptr) 就行了
*
*/
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
@@ -260,6 +261,77 @@ off_t lseek(int fd, offt offset, int whence);
### IO的效率问题
习题:将`mycpy.c`程序进行更改,将`BUFSIZE`的值放大,观察进程消耗的时间,注意性能出现拐点的值以及程序何时段错误。
解答:
`BUFSIZE`作为命令行参数传入,`int bufsize = atoi(argv[3]);`
通过脚本进行试验:
```bash
#!/bin/bash
# 生成一个 5GB 的文件
dd if=/dev/urandom of=/tmp/bigfile bs=1G count=5
# 输入和输出文件的路径
src="/tmp/bigfile"
dst="/tmp/outfile"
# 编译你的程序
gcc -o mycpy_bufsize mycpy_bufsize.c
# 初始化 BUFSIZE
bufsize=512
# 循环,每次 BUFSIZE * 2
while true; do
# 用 time 命令运行你的程序,并将结果重定向到一个临时文件
{ time ./mycpy_bufsize $src $dst $bufsize; } 2> time.txt
# 检查程序的退出状态
if [ $? -ne 0 ]; then
echo "Max BUFSIZE before segfault: $bufsize"
break
fi
# 提取 time 的结果
real_time=$(grep real time.txt | awk -F' ' '{print $2}')
user_time=$(grep user time.txt | awk -F' ' '{print $2}')
sys_time=$(grep sys time.txt | awk -F' ' '{print $2}')
# 输出 BUFSIZE 和 time 的结果
echo "BUFSIZE: $bufsize, Real Time: $real_time, User Time: $user_time, Sys Time: $sys_time"
# BUFSIZE * 2
bufsize=$((bufsize * 2))
done
# 删除临时文件
rm time.txt
rm $src
rm $dst
```
结果:
```bash
wan@SK-20240106UQUX:~/Linux-C-Notes/C13-Linux系统编程/io/sys$ ./time.sh
BUFSIZE: 512, Real Time: 0m7.672s, User Time: 0m0.650s, Sys Time: 0m7.007s
BUFSIZE: 1024, Real Time: 0m5.026s, User Time: 0m0.201s, Sys Time: 0m4.651s
BUFSIZE: 2048, Real Time: 0m3.535s, User Time: 0m0.158s, Sys Time: 0m3.183s
BUFSIZE: 4096, Real Time: 0m2.418s, User Time: 0m0.059s, Sys Time: 0m2.232s
BUFSIZE: 8192, Real Time: 0m2.363s, User Time: 0m0.040s, Sys Time: 0m2.150s
BUFSIZE: 16384, Real Time: 0m2.279s, User Time: 0m0.030s, Sys Time: 0m2.079s
BUFSIZE: 32768, Real Time: 0m2.238s, User Time: 0m0.020s, Sys Time: 0m2.026s
BUFSIZE: 65536, Real Time: 0m2.114s, User Time: 0m0.000s, Sys Time: 0m1.972s
BUFSIZE: 131072, Real Time: 0m2.302s, User Time: 0m0.019s, Sys Time: 0m1.982s
BUFSIZE: 262144, Real Time: 0m2.244s, User Time: 0m0.000s, Sys Time: 0m2.016s
BUFSIZE: 524288, Real Time: 0m2.254s, User Time: 0m0.000s, Sys Time: 0m2.039s
BUFSIZE: 1048576, Real Time: 0m2.249s, User Time: 0m0.010s, Sys Time: 0m2.037s
BUFSIZE: 2097152, Real Time: 0m2.304s, User Time: 0m0.000s, Sys Time: 0m2.108s
BUFSIZE: 4194304, Real Time: 0m2.234s, User Time: 0m0.010s, Sys Time: 0m2.082s
Max BUFSIZE before segfault: 8388608
```
`ulimit -a`中,我的系统的`stack size``8192`,所以`BUFSIZE`不能超过`8192`,否则会段错误。与测试结果一致。
### 文件共享
多个任务共同操作一个文件或者协同完成任务
@@ -325,3 +397,262 @@ int ioctl(int fd, unsigned long request, ... /* arg */);
### /dev/fd/目录
**虚目录**:显示当前进程的文件描述符信息
# 文件系统
`ls`的实现,如`myls -l -a -i -n`
`cmd --长格式 -短格式 非选项的传参`
## 目录和文件
1. 获取文件属性
```c
/**
* 将文件的属性存储到buf中
* stat : 通过文件路径获取属性,面对符号链接文件时,
* 获取的是指向的目标文件的属性
* fstat: 通过文件描述符获取属性
* lstat: 通过文件路径获取属性,面对符号链接文件时,
*/
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
// 文件唯一标识,身份证号
mode_t st_mode; /* protection */
// st_mode: 文件权限+文件类型
// 文件权限
// 七种文件类型dcb-lsp
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
// 在linux下与windows不同size值仅仅是属性
// 不能实际体现占用磁盘大小,详见 big.c
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
```
2. 文件访问权限
`st_mode`是一个16位的二进制数文件类型文件权限特殊权限。
3. `umask`
作用:防止产生权限过松的文件。
`0666 &~umask`
`umask`也是一个终端命令,可以查看和设置。
`mode_t umask(mode_t mask);`
4. 文件权限的更改/管理
```c
/**
* 更改文件权限
*/
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
```
5. 粘住位
t位例如`/tmp`目录。
6. 文件系统:`FAT`, `UFS`
文件或数据的存储格式。
- `FAT`:静态存储的单链表
```c
struct node_st{
int next[N];
char data[N][SIZE];
};
```
- `UFS`
缺点:不善于处理大量的小文件,因为每个文件都有一个`inode`,占用空间。
> 面试题:
> 不用比较比较两个uint32_t的大小
> 使用位图
7. 硬链接,符号链接
- 硬链接
`ln bigfile bigfile_link`
与目录项是同义词
相当于目录项又弄了一份,使用`ls -i`可以看到`inode`号相同。
限制:不能给分区建立,不能给目录建立
- 符号链接
`ln -s bigfile_link bigfile_s`
优点:可以跨分区,可以给目录建立
```c
int link(const char *oldpath, const char *newpath);
/**
* 只有没有引用的数据才会真正删除
* 可以利用这一点创建匿名文件
*/
int unlink(const char *pathname);
int remove(const char *pathname);
/**
* 改变文件的路径或者名字
*/
int rename(const char *oldpath, const char *newpath);
```
8. `utime`
```c
/**
* 更改文件最后读/写的时间
*/
int utime(const char *filename, const struct utimbuf *times);
struct utimbuf {
time_t actime; /* access time */
time_t modtime; /* modification time */
};
struct time_t {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
```
9. 目录的创建和销毁
`mkdir, rmdir`
```c
int mkdir(const char *pathname, mode_t mode);
/**
* 只有目录为空才能删除
*/
int rmdir(const char *pathname);
```
10. 更改当前工作路径
`cd, pwd`
```c
/**
* 改变当前工作路径
* 可以突破假根目录
* 但是不能突破chroot
*/
int chdir(const char *path);
int fchdir(int fd);
/**
* 获取当前工作路径
*/
long getcwd(char *buf, unsigned long size);
```
11. 分析目录/读取目录内容
```c
/**
* 法一
* 解析模式/通配符
*
* @prarm: pattern 匹配模式
* @prarm: flags 匹配标志
* @prarm: errfunc 错误回调函数
* @prarm: pglob 匹配结果
*
* @return 匹配的文件数量
*/
int glob(const char *restrict pattern, int flags,
int (*errfunc)(const char *epath, int eerrno),
glob_t *restrict pglob);
/**
* 释放glob_t结构体
*/
void globfree(glob_t *pglob);
/**
* 与argc, argv类似
*/
typedef struct {
size_t gl_pathc; /* Count of paths matched so far */
char **gl_pathv; /* List of matched pathnames */
size_t gl_offs; /* Slots to reserve in gl_pathv */
} glob_t;
/**
* 法二
*/
/**
* 打开一个目录
* 返回一个指向DIR结构体的指针
* 是堆区,需要 closedir 释放
*/
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
/**
* 关闭一个目录
*/
int closedir(DIR *dirp);
/**
* 读取一个目录
*
* 返回指针指向静态区
*/
struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *restrict dirp,
struct dirent *restrict entry,
struct dirent **restrict result);
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
/**
* 重置一个目录
*/
void rewinddir(DIR *dirp);
void seekdir(DIR *dirp, long offset);
long telldir(DIR *dirp);
/**
* du 命令
* 以字节为单位,统计目录下所有文件的大小
*
*/
```
作业:用另一套函数实现`mydu`
## 系统数据文件和信息
## 进程环境

View File

@@ -0,0 +1,34 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage:...\n");
exit(1);
}
int fd;
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0)
{
perror("open()");
exit(1);
}
// 生成一个5G的文件
// !!! 整数溢出
lseek(fd, 5LL * 1024LL * 1024LL * 1024LL - 1LL, SEEK_SET);
// 写入尾0
write(fd, "", 1);
close(fd);
exit(0);
}

View File

@@ -0,0 +1,41 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
// !!! 不是int
static off_t flen(char *fname)
{
struct stat statres;
if (stat(fname, &statres) < 0)
{
perror("stat()");
exit(1);
}
return statres.st_size;
}
/**
* @brief 获取文件大小
* @details
* Usage: ./flen filename
*
* @param argc
* @param argv
* @return int
*/
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage:...\n");
exit(1);
}
// !!! 通过makefile指定off_t 64位
printf("%lld\n", flen(argv[1]));
exit(0);
}

View File

@@ -0,0 +1,54 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
static int ftype(const char *fname)
{
struct stat statres;
if (stat(fname, &statres) < 0)
{
perror("stat()");
exit(1);
}
if (S_ISREG(statres.st_mode))
return '-';
else if (S_ISDIR(statres.st_mode))
return 'd';
else if (S_ISSOCK(statres.st_mode))
return 's';
else if (S_ISFIFO(statres.st_mode))
return 'p';
else if (S_ISBLK(statres.st_mode))
return 'b';
else if (S_ISCHR(statres.st_mode))
return 'c';
else if (S_ISLNK(statres.st_mode))
return 'l';
else
return '?';
}
/**
* @brief 获取文件类型
* @details
* Usage: ftype <filename>
*
* @param argc
* @param argv
*
* @return int
*/
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage:...\n");
exit(1);
}
printf("%c\n", ftype(argv[1]));
exit(0);
}

View File

@@ -0,0 +1,46 @@
#include <glob.h>
#include <stdio.h>
#include <stdlib.h>
// #define PAT "/etc/a*.conf"
#define PAT "/etc/*" // 不包括隐藏文件 .*
#if 0
static int errfunc_(const char *errpath, int eerrno)
{
puts(errpath);
fprintf(stderr, "ERROR MSG: %s\n", strerror(eerrno));
return 0;
}
#endif
/**
* @brief ls /etc/a*.conf
* @details
*
* @param argc
* @param argv
*
* @return int
*/
int main(int argc, char **argv)
{
glob_t globres;
int err;
err = glob(PAT, 0, NULL, &globres);
if (err)
{
printf("ERROR CODE = %d\n", err);
exit(1);
}
for (int i = 0; i < globres.gl_pathc; i++)
{
puts(globres.gl_pathv[i]);
}
// !!!
globfree(&globres);
exit(0);
}

View File

@@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
printf("argc = %d\n", argc);
// for (int i = 0; i < argc; i++)
for (int i = 0; argv[i] != NULL; i++)
{
puts(argv[i]);
}
exit(0);
}

View File

@@ -0,0 +1,125 @@
#include <glob.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#define PATHSIZE 1024
static int path_noloop(const char *path)
{
char *pos;
/**
* @brief strrchr
* @details
*
* 找到 path 中最后一个 '/' 字符的位置
*/
pos = strrchr(path, '/');
if (NULL == pos)
exit(1);
if ((0 == strcmp(pos + 1, ".") || 0 == strcmp(pos + 1, "..")))
return 0;
return 1;
}
static int64_t mydu(const char *path)
{
// 优化递归
// 将部分与递归无关的变量拿出栈,用 static 修饰
static struct stat statres;
static char nextpath[PATHSIZE];
glob_t globres;
int64_t sum;
int i;
if (lstat(path, &statres) < 0)
{
perror("lstat()");
exit(1);
}
if (!S_ISDIR(statres.st_mode))
{
/* 非目录 */
return statres.st_blocks;
/*
* st_blocks 代表磁盘块数量一个磁盘块大小为512字节
* 所以除以2才表示多少个1k字节
*/
}
/* 目录 */
/* 例如:/aaa/bbb/ccc/ddd/eee/fff */
strncpy(nextpath, path, PATHSIZE);
strncat(nextpath, "/*", PATHSIZE); //[W]
// if (glob(nextpath, 0, NULL, &globres))
// {
// perror("glob(), 1");
// exit(1);
// }
glob(nextpath, 0, NULL, &globres);
// !!! 要考虑 . 和 .. 目录
strncpy(nextpath, path, PATHSIZE);
strncat(nextpath, "/.*", PATHSIZE); //[W]
/*
* 这个警告是因为 strncat 函数的第三个参数(即 PATHSIZE等于目标字符串
* nextpath 的大小。这可能会导致 nextpath 缓冲区溢出,因为 strncat
* 函数会在目标字符串的末尾添加一个 null 字符('\0')。
*/
// !!! APPEND
// if (glob(nextpath, GLOB_APPEND, NULL, &globres))
// {
// perror("glob(), APPEND");
// exit(1);
// }
glob(nextpath, GLOB_APPEND, NULL, &globres);
sum = statres.st_blocks;
for (i = 0; i < globres.gl_pathc; i++)
{
if (path_noloop(globres.gl_pathv[i]))
{
sum += mydu(globres.gl_pathv[i]);
}
}
// !!!
globfree(&globres);
return sum;
}
/**
* @brief mydu
* @details
* Usage: mydu [FILE]...
*
* 用到了递归的思想
*
* !!! 未考虑权限问题
*
* @param argc
* @param argv
*
* @return int
*/
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: mydu [FILE]...\n");
exit(1);
}
printf("%lld\n", mydu(argv[1]) / 2);
exit(0);
}

View File

@@ -0,0 +1,38 @@
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#define PAT "/etc"
/**
* @brief 解析PAT下文件个数
* @details
*
* @param argc
* @param argv
*
* @return int
*/
int main(int argc, char **argv)
{
DIR *dp;
struct dirent *cur;
dp = opendir(PAT);
if (NULL == dp)
{
perror("opendir()");
exit(1);
}
// !!! 注意括号
while ((cur = readdir(dp)) != NULL)
{
puts(cur->d_name);
}
closedir(dp);
exit(0);
}

View File

@@ -35,6 +35,9 @@ int main(int argc, char **argv)
// 修改后发现linesize初始值就是120是后面不够再增加
}
// !!!
free(linebuf);
fclose(fp);
exit(0);
}

View File

@@ -0,0 +1,77 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/**
* @brief
* @details
* Usage: mycpy <src> <dst> <bufsize>
*
* @param argc
* @param argv
*
* @return int
*/
int main(int argc, char **argv)
{
if (argc < 4)
{
fprintf(stderr, "Usage: %s <src> <dst> <bufsize>\n", argv[0]);
exit(1);
}
int bufsize = atoi(argv[3]);
int sfd, dfd;
char buf[bufsize];
int len, ret, pos;
sfd = open(argv[1], O_RDONLY);
if (sfd < 0)
{
perror("open");
exit(1);
}
dfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (dfd < 0)
{
// !!!
close(sfd);
perror("open");
exit(1);
}
while (1)
{
len = read(sfd, buf, bufsize);
if (len < 0)
{
perror("read");
break;
}
if (len == 0)
break;
pos = 0;
while (len > 0)
{
/* 可能没写完被别的中断打断 */
ret = write(dfd, buf + pos, len);
if (ret < 0)
{
perror("write");
exit(1);
}
pos += ret;
len -= ret;
}
}
close(dfd);
close(sfd);
exit(0);
}