diff --git a/C11-数据结构/C11-数据结构.md b/C11-数据结构/C11-数据结构.md index 9c2f695..0988714 100644 --- a/C11-数据结构/C11-数据结构.md +++ b/C11-数据结构/C11-数据结构.md @@ -1,3 +1,15 @@ +# 目录 +- [目录](#目录) +- [数据结构](#数据结构) + - [架构梳理](#架构梳理) + - [静态库与动态库](#静态库与动态库) + - [静态库](#静态库) + - [以链式双向链表的`lib2`为例。](#以链式双向链表的lib2为例) + - [动态库](#动态库) + - [还是以`lib2`为例。](#还是以lib2为例) + - [以链式存储栈为例,`libstack`依赖`libllist`。](#以链式存储栈为例libstack依赖libllist) + + # 数据结构 ## 架构梳理 diff --git a/C13-Linux系统编程/C13-Linux系统编程学习笔记.md b/C13-Linux系统编程/C13-Linux系统编程学习笔记.md index 992a018..1f6176b 100644 --- a/C13-Linux系统编程/C13-Linux系统编程学习笔记.md +++ b/C13-Linux系统编程/C13-Linux系统编程学习笔记.md @@ -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` + + +## 系统数据文件和信息 + + + +## 进程环境 + diff --git a/C13-Linux系统编程/fs/big.c b/C13-Linux系统编程/fs/big.c new file mode 100644 index 0000000..55b8ed3 --- /dev/null +++ b/C13-Linux系统编程/fs/big.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/fs/flen.c b/C13-Linux系统编程/fs/flen.c new file mode 100644 index 0000000..b992bbb --- /dev/null +++ b/C13-Linux系统编程/fs/flen.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +// !!! 不是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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/fs/ftype.c b/C13-Linux系统编程/fs/ftype.c new file mode 100644 index 0000000..4066fea --- /dev/null +++ b/C13-Linux系统编程/fs/ftype.c @@ -0,0 +1,54 @@ +#include +#include +#include + +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 + * + * @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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/fs/glob.c b/C13-Linux系统编程/fs/glob.c new file mode 100644 index 0000000..473990c --- /dev/null +++ b/C13-Linux系统编程/fs/glob.c @@ -0,0 +1,46 @@ +#include +#include +#include + +// #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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/fs/main.c b/C13-Linux系统编程/fs/main.c new file mode 100644 index 0000000..7d05e2b --- /dev/null +++ b/C13-Linux系统编程/fs/main.c @@ -0,0 +1,15 @@ +#include +#include + +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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/fs/mydu.c b/C13-Linux系统编程/fs/mydu.c new file mode 100644 index 0000000..dab4164 --- /dev/null +++ b/C13-Linux系统编程/fs/mydu.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/fs/readdir.c b/C13-Linux系统编程/fs/readdir.c new file mode 100644 index 0000000..76a7b00 --- /dev/null +++ b/C13-Linux系统编程/fs/readdir.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/C13-Linux系统编程/io/stdio/getline.c b/C13-Linux系统编程/io/stdio/getline.c index 9f82c67..e641a29 100644 --- a/C13-Linux系统编程/io/stdio/getline.c +++ b/C13-Linux系统编程/io/stdio/getline.c @@ -35,6 +35,9 @@ int main(int argc, char **argv) // 修改后发现,linesize初始值就是120,是后面不够再增加 } + // !!! + free(linebuf); + fclose(fp); exit(0); } \ No newline at end of file diff --git a/C13-Linux系统编程/io/sys/mycpy_bufsize.c b/C13-Linux系统编程/io/sys/mycpy_bufsize.c new file mode 100644 index 0000000..c0a6768 --- /dev/null +++ b/C13-Linux系统编程/io/sys/mycpy_bufsize.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + + +/** + * @brief + * @details + * Usage: mycpy + * + * @param argc + * @param argv + * + * @return int + */ +int main(int argc, char **argv) +{ + if (argc < 4) + { + fprintf(stderr, "Usage: %s \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); +} \ No newline at end of file