二.文件系统
目录和文件
获取文件属性:
stat:通过文件路径获取属性,面对符号链接文件时获取的是所指向的目标文件
fstat:通过fd获取文件属性
lstat:面对符号链接文件时获取的是符号链接文件的属性
// stat(); -> 获取文件属性
int stat(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 */ //文件对应的模式,文件,目录等
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 */ //以字节为单位的文件容量
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 */ //最后一次改变该文件状态的时间
};
空洞文件:
// lseek(fd,5LL*1024*1024-1,SEEK_SET); 开辟了一个5G大小的空洞文件,虽然st_size是5G,但是其在磁盘中所占的内存块(st_blocks)却很小
文件属性:
st_mode 是一个16位的位图,用于表示文件类型,文件访问权限,以及特殊权文件访问权限
umask命令:
作用:防止产生权限过松的文件
所有的文件的文件权限都满足:0666 & umask
mode_t umask(mode_t umask);
文件权限的更改/管理:
chmod命令 直接更改文件权限
int chmod(const char *path,mode_t mode);
int fchmod(int fd,mode_t mode);
// 参数:指定文件,修改后的权限
粘住位:t位
功能:把某一个命令的使用痕迹保留,为下一次装载该模块的速度更快
文件系统:FAT,UFS
文件或数据的存储和管理
FAT文件系统:静态存储的单链表
一式俩份,非复制内容,是记录操作
问题:1.如果一份FAT表出现了问题,如何判断哪一份表是正确的
2.只能单走向,查询困难
3.承载的文件数量限制
优点:轻量化,小U盘
UFS文件系统:磁盘分区
分区下有若干块组
块组:描述信息+inode位图+数据块位图+inode节点(结构体数组)+数据块(结构体数组)
inode节点:stat,亚数据,隐藏内容,数据块指针组(15个),前12个指针:直接数据块指针,13:一级间接块指针(直接数据块指针数量:大小/指针大小)14:二级间接块指针(直接数据块指针数量:大小/指针大小)15:三级间接块指针(直接数据块指针数量:大小/指针大小)
缺陷:不擅于管理小文件
inode位图:0/1确实inode是否使用,确定inode是否使用,确定数据块的使用
文件名:在目录文件中
硬链接,符号链接:
硬链接=目录项:俩个指针指向同一块内存区域
建立硬链接有限制,不能给分区建立(inode的重复问题),不能目录建立
符号链接:类windows的快捷方式,原文件和符号链接文件不是一个文件
优点:可以跨分区建立,可以给目录建立
int link(const char *oldpath,const char *newpath);
// 封装出ln命令
int unlink(const char *pathname);
// 在磁盘上删除链接的目标文件,是否彻底删除文件不得而知
// 用途:匿名文件
// remove(); -> rm命令
// rename(); -> mv命令
utime:可以更改文件的最后读取时间和最后修改时间
目录的创建和销毁:
// mkdir -> 创建
// rmkdir -> 销毁
更改当前工作路径:
cd:chdir() 封装 会突破假 / 技术
假 / 技术:chroot
long getcwd(char *buf,unsigned int bufsize);
// 封装出pwd命令
分析目录/读取目录内容:
// glob(); -> 分析目录
int glob(const char *restrict pattern, int flags,
int (*errfunc)(const char *epath, int eerrno),
glob_t *restrict pglob);
// 参数:模式,特殊要求,指向函数的指针(函数:获取出错路径),存放解析出的pattern
// globfree() -> 释放出由于glob()申请的空间
void globfree(glob_t *pglob);
// opendir(); -> 通过文件名打开一个流
DIR *opendir(const char *name);
// 返回的目录流指针指在堆中的内存
// fopendir(); -> 通过fd打开一个流
DIR *fopendir(int fd);
// closedir(); -> 关闭一个流
int closedir(DIR *dirp);
// readdir(); -> 读取一个流
struct dirent *readdir(DIR *dirp);
// rewinddir(); -> 参考rewind
void rewinddir(DIR *dirp);
// seekdir(); -> 参考seek
void seekdir(DIR *dirp, long loc);
// telldir(); -> 参考tell
long telldir(DIR *dirp);
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
最后,总结一下,想要获取某目录下(比如a目下)b文件的详细信息,我们应该怎样做?
首先,我们使用opendir
函数打开目录a,返回指向目录a的DIR结构体c。
接着,我们调用readdir
©函数读取目录a下所有文件(包括目录),返回指向目录a下所有文件的dirent结构体d。
然后,我们遍历d,调用stat(d->name,stat *e)
来获取每个文件的详细信息,存储在stat结构体e中。
总体就是这样一种逐步细化的过程,在这一过程中,三种结构体扮演着不同的角色。
dir c = opendir(a)
dirent d readdir(c)
stat e
stat(d->name,&e)
通过上述四步即可在stat结构体e中得到a目录下的b文件的信息
系统的数据文件和信息
/etc/passwd
// getpwnam(); -> 通过文件名获取文件信息
struct passwd *getpwnam(const char *name);
// getpwuid(); -> 通过uid获取文件信息
struct passwd *getpwuid(uid_t uid);
// 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 */
// };
/etc/group
// getgrgid(); -> 通过gid获取组信息
struct group *getgrgid(gid_t gid);
// getgrgrnam(); -> 通过文件名获取组信息
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; /* NULL-terminated array of pointers
// to names of group members */
// };
/etc/shadow
密码加密原理:
hash(哈希) 不是加密,是混淆,不可逆
如原串相同,所得串也相同,防备管理员监守自盗
加密 — 解密
加密:安全?no,逃不过穷举
安全:攻击成本大于收益
口令的随机校验 防止脚本攻击
// getspnam(); -> 加密密码文件工具函数
struct spwd *getspnam(char *name);
// struct spwd
// {
// char *sp_namp; /* user login name */
// char *sp_pwdp; /* encrypted password */
// long int sp_lstchg; /* last password change */
// long int sp_min; /* days until change allowed. */
// long int sp_max; /* days before change required */
// long int sp_warn; /* days warning for expiration */
// long int sp_inact; /* days before account inactive */
// long int sp_expire; /* date when account expires */
// unsigned long int sp_flag; /* reserved for future use */
// };
// crypt(); -> 加密函数
char *crypt(const char *phrase, const char *setting);
// 参数:原串,杂志串,加密方式:默认MD5
时间戳:time_t char * struct tm
// time(); -> 以秒为单位获取时间
time_t time(time_t t);
//用法:
time_t stamp;
time(&stamp);
stamp = time(NULL);
// gmtime();
struct tm *gmtime(const time_t *timep);
// localtime();
struct tm *localtime(const time_t *timep);
/*
gmtime():将time函数得到的秒数转换成一个UTC时间的结构体struct tm,通过此函数gmtime()是0时区,把UTC时间转换成北京时间的话,需要在年数上加1900,月份上加1,小时数加上8。
localtime():得到本地时间,该函数同gmtime函数唯一区别是,在转换小时数不需要加上8了。localtime是将时区考虑在内了,转出的当前时区的时间。但是注意,有些嵌入式设备上被裁减过的系统,时区没有被设置好,导致二者转出来的时间都是0时区的。
*/
// mktime(); -> struct tm 转换为 time_t 可能会改变struct的内容即判断合法性,若合法不修改,若不合法调整为合法
time_t mktime(struct tm *tm);
// strftime(); -> 格式化时间和日期
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr);
// struct tm {
// int tm_sec; /* 秒,范围从 0 到 59 */
// int tm_min; /* 分,范围从 0 到 59 */
// int tm_hour; /* 小时,范围从 0 到 23 */
// int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
// int tm_mon; /* 月,范围从 0 到 11 */
// int tm_year; /* 自 1900 年起的年数 */
// int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
// int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
// int tm_isdst; /* 夏令时 */
// };
进程环境
main函数:
void main(int argc char *argv[]);
进程的终止情况:
正常终止:从main函数返回
调用:exit
调用 :_exit 或者 _Exit
最后一个线程从其启动例程返回
最后一个线程调用:pthread_exit
异常终止:调用:abork
接到一个信号并终止
最后一个线程对其取消请求作出响应
补充:
// atexit(); -> 钩子函数
int atexit(void (*function)(void));
// 在程序正常终止时,程序会调用钩子函数
exit 依赖于 _exit / _Exit 实现
区别:函数 / 系统调用
_exit / _Exit 不会调用钩子函数以及不会对stdio库进行清理
命令行参数的分析:
// getopt();
int getopt(int argc, char *const argv[],
const char *optstring);
// getopt_long(); -> 分析长格式
int getopt_long(int argc, char *const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
环境变量:
export 命令查看本机的环境变量
// getenv(); -> 获取
char *getenv(const char *name);
// (un)setenv(); -> (删除)修改或者增加
int setenv(const char *name,const char *value,int overwrite);
// overwrite : 是否覆写
// putenv(); -> 修改或者增加
int putenv(char *string);
C程序的存储空间布局:pmap指令
库:动态库:
静态库:
手工装载库:
void *dlopen(const char *filename, int flags);
int dlclose(void *handle);
char *dlerror(void);
void *dlsym(void *restrict handle, const char *restrict symbol);
// 记得 Link with -ldl
函数跳转:goto 不能实现跨函数跳转
// setjmp(); -> 设置跳转点
int setjmp(jmp_buf env);
// longjmp(); -> 跳回env并带回val
void longjmp(jmp_buf env,int val);
资源的获取以及控制:
ulimit -a 查看本机的资源量
// getrlimit(); -> 查看
int getrlimit(int resource, struct rlimit *rlp);
// setrlimit(); -> 设置
int setrlimit(int resource, const struct rlimit *rlp);
// 结构体rlp 在对普通用户和root用户的权限不同