Linux下C语言教程-李慧芹老师-第十四章

配套代码笔记仓库

目录

文件系统

ls的实现,如myls -l -a -i -n

cmd --长格式 -短格式 非选项的传参

目录和文件

  1. 获取文件属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 将文件的属性存储到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 */
};
  1. 文件访问权限
    st_mode是一个16位的二进制数,文件类型,文件权限,特殊权限。

  2. umask
    作用:防止产生权限过松的文件。
    0666 &~umask
    umask也是一个终端命令,可以查看和设置。
    mode_t umask(mode_t mask);

  3. 文件权限的更改/管理

1
2
3
4
5
/**
* 更改文件权限
*/
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
  1. 粘住位
    t位,例如/tmp目录。

  2. 文件系统:FAT, UFS
    文件或数据的存储格式。

    1. FAT:静态存储的单链表

      1
      2
      3
      4
      struct node_st{
      int next[N];
      char data[N][SIZE];
      };
    2. UFS
      缺点:不善于处理大量的小文件,因为每个文件都有一个inode,占用空间。

面试题:
不用比较,比较两个uint32_t的大小
使用位图

  1. 硬链接,符号链接

    • 硬链接
      ln bigfile bigfile_link
      与目录项是同义词
      相当于目录项又弄了一份,使用ls -i可以看到inode号相同。

      限制:不能给分区建立,不能给目录建立

    • 符号链接
      ln -s bigfile_link bigfile_s

      优点:可以跨分区,可以给目录建立

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
  1. utime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 更改文件最后读/写的时间
*/
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 */
};
  1. 目录的创建和销毁
    mkdir, rmdir
1
2
3
4
5
6
int mkdir(const char *pathname, mode_t mode);

/**
* 只有目录为空才能删除
*/
int rmdir(const char *pathname);
  1. 更改当前工作路径
    cd, pwd
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 改变当前工作路径
* 可以突破假根目录
* 但是不能突破chroot
*/
int chdir(const char *path);
int fchdir(int fd);

/**
* 获取当前工作路径
*/
long getcwd(char *buf, unsigned long size);
  1. 分析目录/读取目录内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* 法一
* 解析模式/通配符
*
* @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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 通过用户名获取用户信息
*/
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 */
};
  1. /etc/group
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 通过组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 */
};
  1. /etc/shadow
    ll显示root用户也不可读写,但是只有root用户才可读写
    这样是提醒你,即便是root用户,也不要随便读写这个文件

密码

hash - 混淆,不可逆

如果原串一样,hash值也一样

防备管理员监守自盗

加密 - 解密

加密为了安全,攻击成本大于收益

安全?穷举:口令随机校验(第一遍明明对了给你报错,让你连续两遍成功输入正确)

推荐书籍:《应用密码学》

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 获得用户的密码信息
*/
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);
  1. 时间戳
    机器喜欢大整数 time_t
    人类喜欢字符串 char *
    程序员喜欢结构体 struct tm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 从内核获取以秒为单位的一个时戳
* 从 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函数

1
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
    • 接到一个信号并终止
    • 最后一个线程对其取消请求作出响应
1
2
3
4
5
6
7
/**
* 注册一个函数,当进程终止时调用
*
* 钩子函数:逆序执行
* 可以进行资源释放
*/
int atexit(void (*function)(void));// 钩子函数

命令行参数的分析

1
2
3
4
5
6
7
8
9
10
#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命令查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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命令,查看进程空间布局

  • 动态库

  • 静态库

  • 手工装载库

    1
    2
    3
    4
    5
    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无法跨函数跳转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* 设置跳转点
*
* @return 0 说明是在设置跳转点
* @return 非0 说明是通过 longjmp 返回
*/
int setjmp(jmp_buf env);

/*
* 跳转到跳转点
*
* @prarm: env 跳转点
* @prarm: val 传递给 setjmp 的值
*/
void longjmp(jmp_buf env, int val);

资源的获取及控制

ulimit -a

1
2
3
4
5
6
7
8
9
10
// 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 */
};

Linux下C语言教程-李慧芹老师-第十四章
http://sinlatansen.github.io/2024/05/16/Linux下C语言教程-李慧芹老师-第十四章/
作者
fugu
发布于
2024年5月16日
许可协议