文章目录
- 本文内容大部分出自对传智播客linux课程内容的总结和课堂笔记。
- 若有常见命令的详细介绍或linux系统的扩展学习的需要,可以点击此处下载PDF书籍(鸟哥私房菜-基础篇、Linux命令速查手册)。
- 涉及到函数用法,可查找man文档中的详细介绍linux 中文 man离线手册
- 本文原文可参考我在语雀平台的原版笔记点击此处阅读
1. 文件操作
1.1 stat函数
1.1.1 作用
- 获取文件属性(从inode上获取)
- e.g.
stat aaa
1.1.2 头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
1.1.3 函数声明
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
1.1.4 返回值
- 成功返回0,失败返回-1
1.1.5 文件属性(struct stat)
1.1.6 mode_t st_mode
-
该变量占 2byte 共 16位
-
掩码的使用:st_mode & 掩码
- st_mode与某类文件属性信息掩码做按位与操作,然后与宏比较,即可得到相应的信息
-
如果想获取文件属性信息,st_mode&掩码
- e.g. st_mode & S_IFMT可以得到文件类型
-
如果看文件是否有哪个权限,st_mode&掩码&权限的掩码,然后看是否大于0
- e.g. st_mode & S_IRWXU & S_IRUSR 可以看文件所有者是否有读权限
-
- st_mode与某类文件属性信息掩码做按位与操作,然后与宏比较,即可得到相应的信息
-
文件类型(12-15 bit)
- 掩码: S_IFMT 0170000 过滤 st_mode中除文件类型以外的信息
- S_IFSOCK 0140000 套接字
- S_IFLNK 0120000 符号链接(软链接)
- S_IFREG 0100000 普通文件
- S_IFBLK 0060000 块设备
- S_IFDIR 0040000 目录
- S_IFCHR 0020000 字符设备
- S_IFIFO 0010000 管道
-
特殊权限位(9-11 bit)很少用
- S_ISUID 0004000 设置用户ID
- S_ISGID 0002000 设置组ID
- S_ISVTX 0001000 黏住位
-
文件所有者权限(6-8 bit)
- 掩码: S_IRWXU 00700 过滤 st_mode中除文件所有者权限以外的信息
- S_IRUSR 00400 读权限
- S_IWUSR 00200 写权限
- S_IXUSR 00100 执行权限
-
所属组权限(3-5 bit)
- 掩码: S_IRWXG 00070 过滤 st_mode中除所属组权限以外的信息
- S_IRGRP 00040 读权限
- S_IWGRP 00020 写权限
- S_IXGRP 00010 执行权限
-
其他人权限(0-2 bit)
- 掩码: S_IRWXO 00007 过滤 st_mode中除其他人权限以外的信息
- S_IROTH 00004 读权限
- S_IWOTH 00002 写权限
- S_IXOTH 00001 执行权限
1.1.7 特性
- 穿透(跟踪)软链接,读到的是软链接映射到的文件的属性信息
- 相当于用vim打开一个软链接,打开的是软链接指向的文件
1.1.8 练习:利用stat实现类似ls的功能
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
int main(int argc, char** argv)
{
char **fn = argv;
if(argc < 2)
{
printf("miss a parameter: ./a.out filename\n");
exit(1);
}
struct stat st;
int ret = stat(fn[1], &st);
char ftmo[11] = {0};
//获取文件类型和权限
switch(st.st_mode & S_IFMT)
{
case S_IFSOCK: ftmo[0] = 's'; break;
case S_IFLNK: ftmo[0] = 'l'; break;
case S_IFREG: ftmo[0] = '-'; break;
case S_IFBLK: ftmo[0] = 'b'; break;
case S_IFDIR: ftmo[0] = 'd'; break;
case S_IFCHR: ftmo[0] = 'c'; break;
case S_IFIFO: ftmo[0] = 'p'; break;
}
//获取所有者权限
ftmo[1] = (st.st_mode & S_IRWXU & S_IRUSR)?'r':'-';
ftmo[2] = (st.st_mode & S_IRWXU & S_IWUSR)?'w':'-';
ftmo[3] = (st.st_mode & S_IRWXU & S_IXUSR)?'x':'-';
//获取组内权限
ftmo[4] = (st.st_mode & S_IRWXG & S_IRGRP)?'r':'-';
ftmo[5] = (st.st_mode & S_IRWXG & S_IWGRP)?'w':'-';
ftmo[6] = (st.st_mode & S_IRWXG & S_IXGRP)?'x':'-';
//获取其他人权限
ftmo[7] = (st.st_mode & S_IRWXO & S_IROTH)?'r':'-';
ftmo[8] = (st.st_mode & S_IRWXO & S_IWOTH)?'w':'-';
ftmo[9] = (st.st_mode & S_IRWXO & S_IXOTH)?'x':'-';
ftmo[10] = '.';
//硬链接数目
int linknum = st.st_nlink;
//文件所有者
char* uname = getpwuid(st.st_uid)->pw_name;
//文件所属组
char* gname = getgrgid(st.st_gid)->gr_name;
//文件大小
size_t fsize = st.st_size;
//文件日期
//注意:ctime返回的字符串末尾有换行符,需要处理
char* ftime = ctime(&st.st_ctime);
ftime[strlen(ftime)-1] = 0;
//文件名
char* fname = fn[1];
//格式化字符串
char buf[100] = {0};
sprintf(buf, "%s %d %s %s %d %s %s",
ftmo, linknum, uname, gname, fsize, ftime, fname);
printf("%s\n", buf);
return 0;
}
1.2 lstat函数
1.2.1 作用和头文件
- 同stat
1.2.2 函数声明
int lstat(const char *pathname, struct stat *buf);
1.2.3 特性
- 不穿透(跟踪) 软链接,读到的是软链接的属性信息
- 相当于ls -l 和 rm,操作的是软链接的信息
1.3 access函数
1.3.1 作用
- 测试指定文件是否拥有某种权限
1.3.2 头文件
#include <unistd.h>
1.3.3 原型
int access(const char *pathname, int mode);
1.3.4 参数
-
pathname --> 文件名
-
mode --> 权限类别
- R_OK 是否有读权限
- W_OK 是否有写权限
- X_OK 是否有执行权限
- F_OK 测试一个文件是否存在
1.3.5 返回值
- 0 --> 所有欲查核的权限都通过了检查
- -1 --> 有权限被禁止
1.4 chmod函数
1.4.1 作用
- 改变文件的权限
1.4.2 头文件
#include <sys/stat.h>
1.4.3 原型
int chmod( const char *filename, int pmode );
1.4.4 参数
-
filename --> 文件名
-
pmode --> 权限
-
必须是一个8进制数
-
注:可以使用strtol函数将字符串转换为指定进制数 ‘0666’ -> 0666
#include <stdlib.h>
-long int strtol(const char *nptr, char **endptr, int base); 其中endptr
用于测试,可设为0
-
1.4.5 返回值
- 0 --> 改变成功
- -1 --> 失败
1.5 chown函数
1.5.1 作用
- 改变文件的所有者
1.5.2 头文件
#include <unistd.h>
1.5.3 原型
int chown(const char *path, uid_t owner, gid_t group);
1.5.4 参数
-
其中uid 和 gid可以通过 /etc/passwd 获得
-
passwd文件应该怎么看
man 5 passwd
1.5.5 返回值
- 0 --> 成功
- -1 --> 失败
1.6 truncate函数
1.6.1 作用
-
将参数path 指定的文件大小改为参数length 指定的大小。如果原来的文件大小比参数length大,则超过的部分会被删去。
-
扩展或截断一个文件
- 假设文件长度为100
- 第二个参数指定长度为20 ->文件被截断
- 第二个参数指定长度为300 -> 文件被拓展(空洞文件)
-
与lseek的主要区别是扩展时不需要写入内容
1.6.2 头文件
#include <unistd.h>
#include <sys/types.h>
1.6.3 原型
int truncate(const char *path, off_t length);
- path --> 文件路径
- length --> 指定的文件大小
1.6.4 返回值
- 0 --> 执行成功
- -1 --> 执行失败
1.7 链接
1.7.1 link函数
- 作用:创建一个硬链接
- 头文件:
#include <unistd.h>
- 原型:
int link(const char *oldpath, const char newpath);*
1.7.2 symlink 函数
- 作用:创建一个软连接
- 其他同link
1.7.3 readlink 函数
- **作用:**读软链接对应的文件名,不是读内容
- 读非软链接会失败
1.7.4 unlink 函数
-
作用
- 删除一个文件的目录项并减少它的链接数,若成功则返回0,否则返回-1,错误原因存于errno。
- 如果想通过调用这个函数来成功删除文件,你就必须拥有这个文件的所属目录的写和执行权限。
-
头文件:
#include <unistd.h>
-
声明:
int unlink(const char *pathname);
-
使用
-
如果是软链接,删除软链接
-
如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode
-
如果文件硬链接数为0,但有进程已打开该文件,并持有文件描述符,则等该进程关闭该文件时,kernel才真正去删除该文件
- 利用该特性创建临时文件,先open或create创建一个文件,马上unlink此文件
-
-
示例:使用unlink函数制作一个可以自动删除的临时文件。比如看视频时的缓存文件,看完视频以后自动删除。
1.8 rename 函数
- 作用:文件重命名
- 头文件:
stdio.h
- 函数原型:
int rename(const char oldpath, const char newpath);
2. 目录操作
2.1 chdir 函数
- 作用:修改当前进程的路径,相当于cd 修改当前工作目录,改变程序中打开的路径
- 头文件:
#include <unistd.h>
- 函数原型:
int chdir(const char *path);
2.2 getcwd 函数
- 作用:获取当前进程的工作目录
- 头文件:
#include <unistd.h>
- 函数原型:
char *getcwd(char *buf, size_t size);
- 示例:在当前目录中运行程序,然后在进程中切换目录到上级目录并创建一个chdir.txt文件
2.3 mkdir 函数
- 作用:创建目录
- 头文件:
#include <sys/stat.h>
#include <sys/types.h>
- 函数原型:
int mkdir(const char *pathname, mode_t mode);
- 注意:创建的目录需要有执行权限,否则无法进入目录
2.4 rmdir 函数
- 作用:删除一个空目录
- 头文件:
#include <unistd.h>
- 函数原型:
int rmdir(const char *pathname);
2.5 opendir 函数
- 作用:打开一个目录
- 头文件:
#include <sys/types.h>
#include <dirent.h>
- 函数原型:
DIR *opendir(const char *name);
- 返回值
- 成功返回:DIR结构指针,该结构是一个内部结构,保存所打开的目录信息,作用类似于FILE结构
- 出错返回:NULL
2.6 readdir 函数
- 作用:读目录
- 头文件:
#include <dirent.h>
- 函数原型:
struct dirent *readdir(DIR *dirp);
- 返回值
-
其中d_type:
- DT_BLK - 块设备
- DT_CHR - 字符设备
- DT_DIR - 目录
- DT_LNK - 软连接
- DT_FIFO - 管道
- DT_REG - 普通文件
- DT_SOCK - 套接字
- DT_UNKNOWN - 未知
-
-D_BSD_SOURCE 编译时添加宏定义
-
2.7 closedir 函数
- 作用:关闭目录
- 头文件:
#include <sys/types.h>
#include <dirent.h>
- 声明:
int closedir(DIR *dirp);
- 示例:使用opendir\readdir\closedir递归获取指定目录下的普通文件个数
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int getFileNum(char* root)
{
// 打开指定目录
DIR* dir = opendir(root);
if(dir == NULL)
{
perror("opendir");
exit(0);
}
// 递归读取当前目录的普通文件数目
int total = 0;
char path[1024] = {0};
struct dirent* ptr = NULL;
while((ptr = readdir(dir)) != NULL)
{
// 过滤掉. 和 ..目录
if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
{
continue;
}
// 如果当前读取到的是普通文件
if(ptr->d_type == DT_REG)
{
total ++;
}
// 如果当前读取到的是目录
if(ptr->d_type == DT_DIR)
{
// 把目录名存储下来,递归调用读取
sprintf(path, "%s/%s", root, ptr->d_name);
total += getFileNum(path);
}
}
closedir(dir);
return total;
}
int main(int argc, char* argv[])
{
// 获取指定目录下的普通文件数量
int total = getFileNum(argv[1]);
// 打印输出
printf("%s has file number: %d\n", argv[1], total);
return 0;
}
3. fcntl 函数
-
作用:根据文件描述符来操作文件的状态
-
头文件:
#include <fcntl.h>
-
函数原型
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
-
具体功能:
-
复制一个现有的描述符 – cmd
- F_DUPFD
-
获得/设置文件描述符标记 – cmd
- F_GETFD
- F_SETFD
-
获得/设置文件状态标记 – cmd
-
F_GETFL——获取文件状态标记时,arg=0
- 只读打开 O_RDONLY
- 只写打开 O_WRONLY
- 读写打开 O_RDWR
- 执行打开 O_EXEC
- 搜索打开目录 O_SEARCH
- 追加写 O_APPEND
- 非阻塞模式 O_NONBLOCK
-
F_SETFL——设置文件状态标记时,arg为arg|其他标识
- 可更改的几个标识
- O_APPEND
- O_NONBLOCK
-
-
获得/设置异步I/O所有权 – cmd
- F_GETOWN
- F_SETOWN
-
获得/设置记录锁 – cmd
- F_GETLK
- F_SETLK
- F_SETLKW
-
-
示例:改变已经打开的文件的属性
- 在文件打开过程中修改读写权限,如果不使用fcntl的话,必须先关闭文件,然后以新的权限打开文件。
- 打开文件的时候权限为只读;修改文件的只读权限为追加 O_APPEND,指针会自动移到尾部,在文件尾部添加
4. dup和dup2函数
-
作用:复制现有的文件描述符 —— 重定向文件描述符
-
头文件:
#include <unistd.h>
-
声明:
int dup(int oldfd);
int dup2(int oldfd, int newfd);
-
使用:
-
dup
- 返回的是文件描述符表中没有占用的最小的文件描述符
-
dup2
- old 和new不是同一个文件描述符,如果new是一个被打开的文件描述符,在拷贝前先关闭new
- old和new是同一个文件描述符,不会关掉new,直接返回old
-
5. 解决gcc编译过程中c99语法报错的问题
~/.bashrc
alias gcc='gcc -std=gnu99'
6. 索引节点inode
保存的其实是实际的数据的一些信息,这些信息称为“元数据”(也就是对文件属性的描述)。
例如:文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。
- 注意数据分成: 元数据+数据本身
- 注意inode怎样生成的: 每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定(现代OS可以动态变化),一般每2KB就设置一个inode。一般文件系统中很少有文件小于2KB的,所以预定按照2KB分,一般inode是用不完的。所以inode在文件系统安装的时候会有一个默认数量,后期会根据实际的需要发生变化。
- 注意inode号: inode号是唯一的,表示不同的文件。其实在Linux内部的时候,访问文件都是通过inode号来进行的,所谓文件名仅仅是给用户容易使用的。当我们打开一个文件的时候,首先,系统找到这个文件名对应的inode号;然后,通过inode号,得到inode信息,最后,由inode找到文件数据所在的block,现在可以处理文件数据了。
- inode和文件的关系: 当创建一个文件的时候,就给文件分配了一个inode。一个inode只对应一个实际文件,一个文件也会只有一个inode。inodes最大数量就是文件的最大数量。
- FILE* fp = open(“file”);