Linux系统编程3 dentry和inode 参考:https://bean-li.github.io/vfs-inode-dentry/
innode:
本质是结构体,存储文件的属性信息 。如权限、类型、大小、时间、用户、盘块位置等。大多数的inode都存储在磁盘上,少量常用
dentry:
目录项,本质是结构体,存有文件名 和inode指针 。因此一个文件可以有多个dentry项
stat函数 stat函数,用于获取文件属性,即获取的就是inode的内容
1 2 3 #include <sys/types.h> #include <unistd.h> int stat(const char *pathname, struct stat *statbuf);
参数
path:文件路径
buf:传出参数,存放文件属性
返回值
成功:0,失败:-1
stat结构体如下 :
1 2 3 4 5 6 7 8 9 10 11 12 struct stat { dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; dev_t st_rdev; off_t st_size; blksize_t st_blksize; blkcnt_t st_blocks; };
判断文件类型 ,使用对应函数,传入stat结构体中的st_mode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> int main (int argc, char *argv[]) { struct stat sb ; int ret = stat(argy[1 ], &sb); if (ret == -1 ) { perror("stat eror" ); 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 dirln" ); } 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 ; }
link和unlink函数 Linux允许多个目录项共享一个inode,即共享盘块,从而使同一个文件有多个文件名。使用link函数,可以为已经存在的目录创建目录项
1 int link(const char *oldname, const char *newname);
使用unlink可以删除一个文件的目录项
1 int unlink(const char* pathname);
功能详解:unlink从文件系统中中删除一个dentry,若这个dentry是指向这个文件的最后一个链接,并且没有进程处于打开这个文件的状态,则删除这个文件,释放这个文件占用的空间。若这个dentry是指向这个文件的最后一个链接,但是有进程处于打开这个文件的状态,则暂时不删除文件,等到打开这个文件的进程关闭这个文件后,系统才择机删除这个文件。
opendir、readdir函数 opendir打开一个目录文件
1 2 #include <dirent.h> DIR *opendir (const char *name) ;
DIR表示目录流类型。
opendir()用来打开参数name 指定的目录, 并返回目录流指针 ,(相当于文件描述符fd)
对应的关闭时closedir(DIR *name)
readdir函数返回参数dir 目录流的下个目录进入点。
1 struct dirent *readdir(DIR *dirp);
1 2 3 4 5 6 7 8 struct dirent { ino_t d_ino; //d_ino 此目录项的inode ff_t d_off; //d_off 目录文件开头至此目录进入点的位移 signed short int d_reclen; //d_reclen _name 的长度, 不包含NULL 字符 unsigned char d_type; //d_type d_name 所指的文件类型 d_name 文件名 har d_name[256]; };
返回值 :成功则返回下个目录项指针 。读取到目录文件尾则返回NULL,error不变,读取错误的话返回NULL,并且error被设置
每次读一个项,就会有目录流读取位置就会往后偏移一项,使下次使用readdir可以读下一个目录项指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.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 ; }
rewinddir函数
1 void rewinddir(DIR *dir)
rewinddir()用来设置参数dir 目录流目前的读取位置为原来开头的读取位置.
telldir函数
1 off_t telldir(DIR *dir);
telldir()返回参数dir 目录流目前的读取位置。此返回值代表距离目录文件开头的偏移量,返回值返回下个读取位置。
seekdir函数
1 void seekdir(DIR * dir, off_t offset);
seekdir()用来设置参数dir 目录流目前的读取位置, 在调用readdir()时便从此新位置开始读取. 参数offset 代表距离目录文件开头的偏移量。
总结:实现ls功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <dirent.h> void Read_file (char *, const int ) ;void is_file (char *,const int ) ;void Read_file (char *name, const int depth) { DIR *dp; struct dirent *sdp ; if ((dp = opendir(name)) == NULL ) { perror("opendir error" ); exit (1 ); } while ((sdp = readdir(dp)) != NULL ) { if (strcmp (sdp->d_name,"." )==0 ||strcmp (sdp->d_name,".." )==0 ) continue ; char file_true_path[1000 ]; char lastChar = name[strlen (name)-1 ]; if (lastChar=='/' ) sprintf (file_true_path,"%s%s" ,name,sdp->d_name); else sprintf (file_true_path,"%s/%s" ,name,sdp->d_name); is_file(file_true_path,depth); } } void is_file (char *path, const int depth) { struct stat sb ; if (stat(path, &sb)==-1 ) { perror("stat error" ); exit (1 ); } if (S_ISDIR(sb.st_mode)) { for (int i=1 ;i<=3 *depth;i++) printf (" " ); printf ("%s\n" ,path); Read_file(path,depth+1 ); } else { for (int i=1 ;i<=3 *depth;i++) printf (" " ); printf ("%s %8ld\n" ,path, sb.st_size); } } int main (int *argc, char *argv[]) { if (argc == 1 ) is_file("." ,1 ); is_file(argv[1 ],1 ); return 0 ; }
重定向(dup和dup2) 1 2 #include <unistd.h> int dup (int oldfd) ;
返回值:成功返回新的文件描述符
dup函数为oldfd指向的文件创建一个新的文件描述符副本,它们指向相同的打开文件描述符(参见 open(2)),因此共享文件偏移量和文件状态标志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fd; if((fd = open(argv[1],O_RDWR|O_APPEND))==-1) { perror("open error"); exit(1); } int newfd = dup(fd); //为一个相同文件返回新的文件描述符 printf("newfd = %d\n",newfd); if (write(newfd, "1234567", 7) == -1) { perror("write error"); exit(1); } return 0; }
1 2 #include <unistd.h> int dup(int oldfd, int newfd); //oldfd是已有的文件描述符,newfd是新的文件描述符
dup2()
系统调用执行与 dup()
类似的任务,但是不使用未使用的最低文件描述符号,而是使用在 newfd
中指定的文件描述符号。如果文件描述符 newfd
之前已经打开,则在被重用之前会被静默关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main (int argc, char *argv[]) { int fd; if ((fd = open(argv[1 ],O_RDWR|O_APPEND))==-1 ) { perror("open error" ); exit (1 ); } int newfd = dup2(fd,STDOUT_FILENO); printf ("------------------\n" ); return 0 ; }