1. 文件操作
1.1 stat函数
1.1.1 作用
- 获取文件属性(从inode上获取)
- e.g.
stat aaa
1.1.2 头文件
#include <sys/types.h>
#include <sys/stat.h>
-
1.1.3 函数声明
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
1.1.4 返回值
-
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 可以看文件所有者是否有读权限
- 文件类型(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)
穿透(跟踪)软链接,读到的是软链接映射到的文件的属性信息
-
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 作用和头文件
-
1.2.2 函数声明
int lstat(const char *pathname, struct stat *buf);
1.2.3 特性
不穿透(跟踪) 软链接,读到的是软链接的属性信息
-
1.3 access函数
1.3.1 作用
-
1.3.2 头文件
include
1.3.3 原型
int access(const char *pathname, int mode);
1.3.4 参数
pathname —> 文件名
mode —> 权限类别
0 —> 所有欲查核的权限都通过了检查
-
1.4 chmod函数
1.4.1 作用
-
1.4.2 头文件
-
1.4.3 原型
int chmod( const char *filename, int pmode );
1.4.4 参数
filename —> 文件名
pmode —> 权限
0 —> 改变成功
-
1.5 chown函数
1.5.1 作用
-
1.5.2 头文件
-
1.5.3 原型
int chown(const char *path, uid_t owner, gid_t group);
1.5.4 参数
其中uid 和 gid可以通过/etc/passwd获得
passwd文件应该怎么看
0 —> 成功
-
1.6 truncate函数
1.6.1 作用
将参数path 指定的文件大小改为参数length 指定的大小。如果原来的文件大小比参数length大,则超过的部分会被删去。
- 扩展或截断一个文件
- 假设文件长度为100
- 第二个参数指定长度为20 ->文件被截断
- 第二个参数指定长度为300 -> 文件被拓展(空洞文件)
-
1.6.2 头文件
#include <unistd.h>
-
1.6.3 原型
int truncate(const char *path, off_t length);
- path —> 文件路径
-
1.6.4 返回值
0 —> 执行成功
-
1.7 链接
1.7.1 link 函数
作用:创建一个硬链接
- 头文件:
#include <unistd.h>
原型:
int link(const char _oldpath, const char newpath);_
1.7.2 symlink 函数
作用:创建一个软连接
-
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);
返回值
作用:读目录
- 头文件:
#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递归获取指定目录下的普通文件个数
```c
include
include
include
include
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
- F_GETFL——获取文件状态标记时,arg=0
- 获得/设置异步I/O所有权 — cmd
- F_GETOWN
- F_SETOWN
- 获得/设置记录锁 — cmd
- F_GETLK
- F_SETLK
- F_SETLKW
- 复制一个现有的描述符 — cmd
- 示例:改变已经打开的文件的属性
- 在文件打开过程中修改读写权限,如果不使用fcntl的话,必须先关闭文件,然后以新的权限打开文件。
- 打开文件的时候权限为只读;修改文件的只读权限为追加 O_APPEND,指针会自动移到尾部,在文件尾部添加
4. dup和dup2函数
- 作用:复制现有的文件描述符 —— 重定向文件描述符
- 头文件:
#include <unistd.h>
- 声明:
int dup(int oldfd);
int dup2(int oldfd, int newfd);
使用:
~/.bashrc
-
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”);