文件存储
inode
本质是结构体,存储文件的属性信息如:权限、类型、大小、时间、用户、盘块位置等。也叫做文件属性管理结构,大多数inode存储在磁盘上,少量常用、近期使用的inode会被缓存到内存中。
dentry
目录项,本质是结构体,有两个重要的成员变量:1.文件名 2.inode 。文件内容(data)保存在磁盘盘块中。
文件操作
stat函数
#include <sys/stat.h>
#include <unistd.h>
原型:int stat(const char *pathname, struct stat *buf);
获取文件属性,path文件属性,buf传出参数存放文件属性,成功返回0,失败返回-1。
使用ln -s 创建软连接,然后查看对应的连接文件时,stat函数会”穿透“符号连接,使用stat判断文件类型时会显示连接的原文件属性,而使用lstat函数则不会出现此情况。
使用lstat函数查看文件类型:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
int main(int argc, char *argv[]){
struct stat sb;
int ret = lstat(argv[1], &sb);
if(ret == -1){
perror("stat error");
exit(1);
}
if(S_ISREG(sb.st_mode)) {
printf("It's a regular\n");
} else if (S_ISDIR(sb.st_mode)) {
printf("It's a dir\n");
} else if (S_ISFIFO(sb.st_mode)) {
printf("It's a pipe\n");
} else if (S_ISLNK(sb.st_mode)) {
printf("It's a sym link\n");
}
return 0;
}
获取文件大小:buf.st_size
获取文件类型:buf.st_mode
获取文件权限:buf.st_mode
link&unlink函数
#include <unistd.h>
原型:int link(const char *oldpath, const char *newpath);
int unlink(const char*pathname);
返回值:成功0,失败-1 并设置errno
为已存在的文件创建目录项以及删除一个文件的目录项
使用link和unlink实现文件改名操作:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
int main(int argc, char *argv[]){
link(argv[1], argv[2]);
unlink(argv[1]);
return 0;
}
Linux下删除文件的机制是不断将st_nlink减一直到0,无目录项对应的文件将会被操作系统择机释放。删除文件的操作只是让文件具备了被释放的条件。
unlink函数的特征:清除文件时,如果文件的硬链接数到0,没有dentry对应,但该文件不会被马上释放,要等到所有打开该文件的进程关闭该文件,系统才会挑时间释放该文件。
隐式回收
当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。系统的这一特性称之为隐式回收系统资源。(不可依赖该特性)
文件、目录权限
注:目录文件也是文件,其文件内容是该目录下所有子文件的目录项
目录操作
opendir函数
原型:DIR *opendir(const char *name);
根据传入的目录名打开一个目录(库函数),成功返回指向该目录的结构体指针,失败返回NULL
closedir函数
原型:int closedir(DIR *dirp);
关闭打开的目录,成功返回0,失败返回-1并设置errno为相应值
readdir函数
原型:struct dirent *readdir(DIR *dirp);
读取目录,成功返回目录项结构体指针,失败返回NULL并设置errno为相应值
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};
见文件名大小,可知文件名长度最长为256-1=255个字节(不包括'\0')
getcwd函数
原型:char *getcwd(char *buf, size_t size);
获取进程当前工作目录,成功:buf保存当前进程工作目录位置;失败:返回NULL
chdir函数(系统调用)
原型:int chdir(const char *path);
改变当前进程工作目录,成功返回0,失败返回-1并设置errno为相应值
使用opendir和readdir实现命令ls的功能:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <dirent.h>
int main(int argc, char *argv[]){
DIR *dp;
struct dirent *sdp;
dp = opendir(argv[1]);
if(dp == NULL){
perror("opendir error");
exit(1);
}
while((sdp = readdir(dp)) != NULL){
if(strcmp(sdp->d_name, ".") == 0 || strcmp(sdp->d_name, "..") == 0)
continue; //去除对于隐藏文件的显示
printf("%s\t", sdp->d_name);
}
printf("\n");
closedir(dp);
return 0;
}
实现递归遍历ls -R的效果且打印文件大小:
step1. 判断命令行参数,获取用户要查询的目录名 argv[1] argc == 1 ——> ./
step2. 判断用户指定的是否是目录 stat S_ISDIR() 封装一个函数 isFile
step3. 读目录 opendir() readdir() closedir()
递归的方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <dirent.h>
#include <sys/stat.h>
#define PATH_LENGTH 256
void isFile(char *name);
// 打开目录读取,处理目录
void read_dir(char *dir){
char path[256];
DIR *dp;
struct dirent *sdp;
dp = opendir(dir);
if(dp == NULL){
perror("opendir error");
return;
}
// 读取目录项
while((sdp = readdir(dp)) != NULL){
if(strcmp(sdp->d_name, ".") ==0 || strcmp(sdp->d_name, "..") == 0){
continue;
}
// 目录项本身不可访问,拼接目录/目录项
sprintf(path, "%s/%s", dir, sdp->d_name);
// 判断文件类型,目录则递归进入,文件显示名字和大小
isFile(path);
}
closedir(dp);
return;
}
void isFile(char *name){
int ret = 0;
struct stat sb;
// 获取文件属性,判断类型
ret = stat(name, &sb);
if(ret == -1){
perror("stat error");
return;
}
if(S_ISDIR(sb.st_mode)){
read_dir(name);
}
printf("%10s\t%ld\n", name, sb.st_size);
return;
}
int main(int argc, char *argv[]){
// 判断命令行参数
if(argc == 1){
isFile(".");
} else {
isFile(argv[1]);
}
return 0;
}
回调的方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <dirent.h>
#include <sys/stat.h>
#define PATH_LENGTH 256
void isFile(char *name);
void read_dir(char *dir, void(*func)(char*)){
char path[256];
DIR *dp;
struct dirent *sdp;
dp = opendir(dir);
if(dp == NULL){
perror("opendir error");
return;
}
while((sdp = readdir(dp)) != NULL){
if(strcmp(sdp->d_name, ".") ==0 || strcmp(sdp->d_name, "..") == 0){
continue;
}
sprintf(path, "%s/%s", dir, sdp->d_name);
//isFile(path);
(*func)(path);
}
closedir(dp);
return;
}
void isFile(char *name){
int ret = 0;
struct stat sb;
ret = stat(name, &sb);
if(ret == -1){
perror("stat error");
return;
}
if(S_ISDIR(sb.st_mode)){
read_dir(name, isFile);
}
printf("%10s\t%ld\n", name, sb.st_size);
return;
}
int main(int argc, char *argv[]){
if(argc == 1){
isFile(".");
} else {
isFile(argv[1]);
}
return 0;
}
重定向
dup函数
原型:int dup(int oldfd);
使用现有的文件描述符拷贝一个新的文件描述符,函数调用前后两个文件描述符指向同一文件
成功返回一个新的文件描述符,失败返回-1并设置errno为相应值
dup2函数
原型:int dup2(int oldfd, int newfd);
实现命令行“重定向”功能。使得原来指向某文件的文件描述符,指向其他指定文件
成功:返回新的文件描述符newfd,若oldfd有效,则返回的文件描述符和oldfd指向同一文件
失败:若oldfd无效,关闭newfd,返回-1并设置errno为相应值
dup2之后执行write newfd将对oldfd文件进行操作。
fcntl实现dup描述符:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
int main(int argc, char *argv[]){
int fd1 = open(argv[1], O_RDWR);
printf("fd1 = %d\n",fd1);
int newfd = fcntl(fd1, F_DUPFD, 0); // 0被占用,fcntl使用文件描述符表中可用的最小文件描述符返回
printf("newfd = %d\n", newfd);
int newfd2 = fcntl(fd1, F_DUPFD, 7); // 7未被占用,返回=7的文件描述符
printf("newfd2 = %d\n", newfd2);
int ret = write(newfd2, "YYYY", 7);
printf("ret=%d\n", ret);
return 0;
}