配套代码笔记仓库

目录

文件系统

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

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

目录和文件

  1. 获取文件属性
/**
 *  将文件的属性存储到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. 文件权限的更改/管理
/**
 * 更改文件权限
*/
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
  1. 粘住位
    t位,例如/tmp目录。
  2. 文件系统:FAT, UFS
    文件或数据的存储格式。

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

      struct node_st{
        int  next[N];
          char data[N][SIZE];
      };
      1. UFS
        缺点:不善于处理大量的小文件,因为每个文件都有一个inode,占用空间。
面试题:
不用比较,比较两个uint32_t的大小
使用位图
  1. 硬链接,符号链接

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

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

  • 符号链接
    ln -s bigfile_link bigfile_s

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

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
/**
 *  更改文件最后读/写的时间
*/
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
int mkdir(const char *pathname, mode_t mode);

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

/**
 *  获取当前工作路径
*/
long getcwd(char *buf, unsigned long size);
  1. 分析目录/读取目录内容
/**
 *  法一
 *  解析模式/通配符
 * 
 * @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
/**
 *  通过用户名获取用户信息
*/
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
/**
 *  通过组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值也一样

防备管理员监守自盗

加密 - 解密

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

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

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

/**
  * 获得用户的密码信息
*/
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
/**
 *  从内核获取以秒为单位的一个时戳
 *  从 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函数

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

命令行参数的分析

#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命令查看

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命令,查看进程空间布局

  • 动态库
  • 静态库
  • 手工装载库

    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无法跨函数跳转。

/*
 *  设置跳转点
 *  
 * @return  0  说明是在设置跳转点
 * @return 非0 说明是通过 longjmp 返回
 */
int setjmp(jmp_buf env);

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

资源的获取及控制

ulimit -a

// 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 */
};