570 lines
13 KiB
Markdown
570 lines
13 KiB
Markdown
# 目录
|
||
|
||
- [目录](#目录)
|
||
- [文件系统](#文件系统)
|
||
- [目录和文件](#目录和文件)
|
||
- [系统数据文件和信息](#系统数据文件和信息)
|
||
- [进程环境](#进程环境)
|
||
- [`main`函数](#main函数)
|
||
- [进程的终止](#进程的终止)
|
||
- [命令行参数的分析](#命令行参数的分析)
|
||
- [环境变量](#环境变量)
|
||
- [C程序的存储空间布局](#c程序的存储空间布局)
|
||
- [库](#库)
|
||
- [函数之间正常的跳转](#函数之间正常的跳转)
|
||
- [资源的获取及控制](#资源的获取及控制)
|
||
|
||
# 文件系统
|
||
|
||
类`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`
|
||
文件或数据的存储格式。
|
||
1. `FAT`:静态存储的单链表
|
||
|
||
```c
|
||
struct node_st{
|
||
int next[N];
|
||
char data[N][SIZE];
|
||
};
|
||
```
|
||
|
||
2. `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`
|
||
|
||
## 系统数据文件和信息
|
||
|
||
> 不同环境可能有区别,以具体查询为准,这里以Linux为例
|
||
|
||
1. `/etc/passwd`
|
||
|
||
```c
|
||
/**
|
||
* 通过用户名获取用户信息
|
||
*/
|
||
struct passwd *getpwuid(uid_t uid);
|
||
|
||
/**
|
||
* 通过用户ID获取用户信息
|
||
*/
|
||
struct passwd *getpwnam(const char *name);
|
||
|
||
struct passwd {
|
||
char *pw_name; /* username */
|
||
char *pw_passwd; /* user password */
|
||
uid_t pw_uid; /* user ID */
|
||
gid_t pw_gid; /* group ID */
|
||
char *pw_gecos; /* user information */
|
||
char *pw_dir; /* home directory */
|
||
char *pw_shell; /* shell program */
|
||
};
|
||
```
|
||
|
||
2. `/etc/group`
|
||
|
||
```c
|
||
/**
|
||
* 通过组ID获取组信息
|
||
*/
|
||
struct group *getgrgid(gid_t gid);
|
||
|
||
/**
|
||
* 通过组名获取组信息
|
||
*/
|
||
struct group *getgrnam(const char *name);
|
||
|
||
struct group {
|
||
char *gr_name; /* group name */
|
||
char *gr_passwd; /* group password */
|
||
gid_t gr_gid; /* group ID */
|
||
char **gr_mem; /* group members */
|
||
};
|
||
```
|
||
|
||
3. `/etc/shadow`
|
||
`ll`显示**root**用户也不可读写,但是只有**root**用户才可读写
|
||
这样是提醒你,即便是**root**用户,也不要随便读写这个文件
|
||
|
||
> 密码
|
||
>
|
||
> hash - 混淆,不可逆
|
||
>
|
||
> 如果原串一样,hash值也一样
|
||
>
|
||
> 防备管理员监守自盗
|
||
>
|
||
> 加密 - 解密
|
||
>
|
||
> 加密为了安全,攻击成本大于收益
|
||
>
|
||
> 安全?穷举:口令随机校验(第一遍明明对了给你报错,让你连续两遍成功输入正确)
|
||
>
|
||
> 推荐书籍:《应用密码学》
|
||
|
||
```c
|
||
/**
|
||
* 获得用户的密码信息
|
||
*/
|
||
struct *spwd getspnam(const char *name);
|
||
|
||
/**
|
||
* 加密密码
|
||
*
|
||
* @prarm: key 密码
|
||
* @prarm: salt 盐 杂字串
|
||
*
|
||
* 默认 md5 加密方式
|
||
*/
|
||
char *crypt(const char *key, const char *salt);
|
||
|
||
struct spwd {
|
||
char *sp_namp; /* login name */
|
||
char *sp_pwdp; /* encrypted password */
|
||
long sp_lstchg; /* last change */
|
||
long sp_min; /* min days between changes */
|
||
long sp_max; /* max days between changes */
|
||
long sp_warn; /* warning days before password
|
||
expires */
|
||
long sp_inact; /* days before account inactive */
|
||
long sp_expire; /* days since 1970-01-01 until account
|
||
expires */
|
||
unsigned long sp_flag; /* reserved */
|
||
};
|
||
|
||
/**
|
||
* 输入提示符
|
||
*/
|
||
char *getpass(const char *prompt);
|
||
```
|
||
|
||
4. 时间戳
|
||
机器喜欢大整数 `time_t`
|
||
人类喜欢字符串 `char *`
|
||
程序员喜欢结构体 `struct tm`
|
||
|
||
```c
|
||
/**
|
||
* 从内核获取以秒为单位的一个时戳
|
||
* 从 UTC 1970年1月1日0时0分0秒 到现在的秒数
|
||
*/
|
||
time_t time(time_t *t);
|
||
|
||
// eg: 两种用法
|
||
time_t stamp;
|
||
time(&stamp);
|
||
stamp=time(NULL);
|
||
|
||
/**
|
||
* 将时间戳转换为结构体
|
||
*/
|
||
struct tm *gmtime(const time_t *timep);
|
||
struct tm *localtime(const time_t *timep);
|
||
|
||
sturct tm {
|
||
int tm_sec; /* seconds */
|
||
int tm_min; /* minutes */
|
||
int tm_hour; /* hours */
|
||
int tm_mday; /* day of the month */
|
||
int tm_mon; /* month */
|
||
int tm_year; /* year */
|
||
int tm_wday; /* day of the week */
|
||
int tm_yday; /* day in the year */
|
||
int tm_isdst; /* daylight saving time */
|
||
/* daylight 夏令时调整 */
|
||
};
|
||
|
||
/**
|
||
* 将结构体转换为时间戳
|
||
* ! 没有 const,可能更改 tm
|
||
*/
|
||
time_t mktime(struct tm *tm);
|
||
|
||
/**
|
||
* 格式化输出时间
|
||
*/
|
||
size_t strftime(char *s, size_t max, const char *format,
|
||
const struct tm *tm);
|
||
|
||
// eg
|
||
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
|
||
```
|
||
|
||
## 进程环境
|
||
|
||
### `main`函数
|
||
|
||
```c
|
||
int main(int argc, char *argv[]);
|
||
```
|
||
|
||
### 进程的终止
|
||
|
||
1. 正常终止:
|
||
|
||
- 从`main`函数返回
|
||
- 调用`exit`
|
||
`void exit(int status);`
|
||
status & 0377 有符号的char -128~127
|
||
|
||
- 调用`_exit`或者`_Exit`(系统调用)
|
||
|
||
> `exit`与`_exit _Exit`的区别
|
||
> `_exit`不执行`atexit`注册的函数,不刷新`stdio`缓冲区
|
||
> 这样可以防止错误扩散
|
||
|
||
- 最后一个线程从其启动例程返回
|
||
- 最后一个线程调用了`pthread_exit`
|
||
|
||
2. 异常终止
|
||
|
||
- 调用`abort`
|
||
- 接到一个信号并终止
|
||
- 最后一个线程对其取消请求作出响应
|
||
|
||
```c
|
||
/**
|
||
* 注册一个函数,当进程终止时调用
|
||
*
|
||
* 钩子函数:逆序执行
|
||
* 可以进行资源释放
|
||
*/
|
||
int atexit(void (*function)(void));// 钩子函数
|
||
```
|
||
|
||
### 命令行参数的分析
|
||
|
||
```c
|
||
#include <unistd.h>
|
||
|
||
extern char *optarg; // 选项参数
|
||
// optind: 下一个要处理的参数的索引
|
||
extern int optind, opterr, optopt;
|
||
|
||
int getopt(int argc, char *const argv[], const char *optstring);
|
||
|
||
int getopt_long(int argc, char *const argv[], const char *optstring,
|
||
const struct option *longopts, int *longindex);
|
||
```
|
||
|
||
### 环境变量
|
||
|
||
**KEY = VALVE**
|
||
可以通过`export`命令查看
|
||
|
||
```c
|
||
char *getenv(const char *name);
|
||
|
||
/*
|
||
* change or add
|
||
*
|
||
* @prarm: overwrite 是否覆盖
|
||
*
|
||
* 覆盖时是释放原来的空间,重新分配
|
||
*/
|
||
int setenv(const char *name, const char *value, int overwrite);
|
||
int unsetenv(const char *name);
|
||
|
||
/*
|
||
* 和getenv一样的作用,change or add
|
||
* 用法不一样,且没有const修饰
|
||
*/
|
||
int putenv(char *string);
|
||
```
|
||
|
||
### C程序的存储空间布局
|
||
|
||
`pmap`命令,查看进程空间布局
|
||
|
||
### 库
|
||
|
||
- 动态库
|
||
|
||
- 静态库
|
||
|
||
- 手工装载库
|
||
|
||
```c
|
||
void *dlopen(const char *filename, int flag);
|
||
char *dlerror(void);
|
||
int dlclose(void *handle);
|
||
void *dlsym(void *handle, const char *symbol);
|
||
// Link with -ldl
|
||
```
|
||
|
||
### 函数之间正常的跳转
|
||
|
||
`goto`无法跨函数跳转。
|
||
|
||
```c
|
||
/*
|
||
* 设置跳转点
|
||
*
|
||
* @return 0 说明是在设置跳转点
|
||
* @return 非0 说明是通过 longjmp 返回
|
||
*/
|
||
int setjmp(jmp_buf env);
|
||
|
||
/*
|
||
* 跳转到跳转点
|
||
*
|
||
* @prarm: env 跳转点
|
||
* @prarm: val 传递给 setjmp 的值
|
||
*/
|
||
void longjmp(jmp_buf env, int val);
|
||
```
|
||
|
||
### 资源的获取及控制
|
||
|
||
`ulimit -a`
|
||
|
||
```c
|
||
// get/set resource limits
|
||
int getrlimit(int resource, struct rlimit *rlim);
|
||
int setrlimit(int resource, const struct rlimit *rlim);
|
||
|
||
// 普通用户不能设置超过硬限制
|
||
// root 用户可以 升高/降低 硬限制
|
||
struct rlimit {
|
||
rlim_t rlim_cur; /* soft limit */
|
||
rlim_t rlim_max; /* hard limit */
|
||
};
|
||
```
|