ch7&ch8学习笔记
知识点归纳
一块存储设备,可以分为几个逻辑单元,称为分区。各个分区均开一格式化为特定的文件系统。
分区表位于第一个扇区的字节偏移446处,该扇区成为设备的主引导记录(MBR)
如果分区是扩展类型,那么它可以划分为更多的分区
Linux一直使用EXT2作为默认文件系统,EXT3是EXT2的扩展,增加的主要内容是日志文件
Block#0 是引导块,文件系统不会使用它,它用于容纳从磁盘引导操作系统的引导程序
Block#1 是超级块,用于容纳关于整个文件系统的信息
Block#2 是块组描述符,EXT2将磁盘块分成几个组,每组有8192个块,每组用一个块组描述符结构体描述
Block#8 是块位图,用于表示某种项的位序列,例如磁盘块或索引节点,位图用于分配和回收项。
Block#9 是索引节点位图,索引节点用于代表一个文件的数据结构
Block#10 是索引(开始)节点块,索引节点大小用于平均分割块大小,所以每个索引节点块都包含整数个索引节点。
数据块 是紧跟在索引节点块后面的文件存储块。
目录条目:dir_entry结构体{inode,rec_len,name_len,file_type,name}
内核的系统调用处理程序 根据系统调用编号 将调用路由到一个相应的内核函数。当进程结束执行内核函数时,会返回到用户模式,返回值大于等于0,表示成功,返回值等于-1表示失败。如果失败,errno变量会记录错误编号,它们会被映射到描述错误原因的字符串,使用<errno.h>里的strerror()函数。
常用的系统调用有
系统调用 | 作用 |
---|---|
stat(fstat,lstat) | 获取文件状态信息 |
open | 打开文件 |
close | 关闭打开的文件描述符 |
read | 读取文件 |
write | 写入文件 |
lseek | 重定位偏移量 |
dup | 将文件描述符复制到可用的最小描述符编号中 |
dup2 | 将旧文件描述符 复制到新文件描述符 |
link | 将新文件硬链接到旧文件 |
unlink | 取消链接,如果链接数为0,则删去文件 |
symlink | 创建一个符号链接 |
readlink | 读取符号链接文件的内容 |
umask | 设置文件创建掩码 |
链接文件:分为硬链接和软链接。软链接相当于windows里的快捷方式,硬链接文件会共享文件系统中相同的索引节点(inode)
lstat会返回链接文件本身的信息
stat结构体
struct stat{
dev_t st_dev ; / * device * /
ino_t st_ino; / * inode * /
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 type (if inode device)*/
off_t st_size; /* total size, in bytes * /
u32 st_blksize; / * blocksize for filesystem I/O */
u32 st_blocks; / *number of 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 change * /
};
st_mode与宏函数
索引节点结构体
struct ext2_inode{
u16 i_mode;
u16 i_uid;
u32 i_size;
u32 i_atime;
u32 i_ctime;
u32 i_mtime;
u32 i_dtime;
u16 i_gid;
u16 i_links_count;
u32 i_blocks;
u32 i_flags;
u32 i_reserved1 ;
u32 i_block[15];
u32 pad[7];
}
目录也是一个文件,我们应该能像其他普通文件一样,打开一个目录,然后读写它的内容。<dirent.h> opendir()和readdir()
问题与解决思路
- 由用户ID获取用户名
ID => struct passwd => .pw_name
//uid
struct passwd *user;
user = getpwuid(fstat.st_uid);
printf("%s\t",user->pw_name);
- 用组ID获取组名
ID => struct group => .gr_name
// gid
struct group *gdata;
gdata = getgrgid(fstat.st_gid);
printf("%s ", gdata->gr_name);
- 删除路径中的目录,获得文件名
char *basename(char *path);
char *filename = basename(path);
- 格式化输出st_mtime
struct tm *localtime(const time_t *timer)
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; /* 夏令时 */
};
实践内容
输出目录下的文件(不递归进入目录)
1 #include <dirent.h>
2 #include <stdio.h>
3
4 int main(int argc,char *argv[]){
5 struct dirent *ep ;
6 DIR *dp = opendir(argv[1]);
7 while (ep = readdir(dp) ){
8 printf( "%s ", ep->d_name);
9 }
10 printf("\n");
11 return 0;
12 }
ls-l实现——myll
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/stat.h>
5 #include <time.h>
6 #include <sys/types.h>
7 #include <dirent.h>
8 #include <unistd.h>
9 #include <libgen.h>
10 #include <pwd.h>
11 #include <grp.h>
12
13 int ls_file(char *fname){
14 //get file status
15 struct stat fstat;
16 int r, i;
17 char ftime[64];
18 if((r = lstat(fname,&fstat)) < 0){
19 perror("lstat file failed ");
20 exit(1);
21 }
22 //get file type
23 if(S_ISREG(fstat.st_mode)){
24 printf( "%c" ,'-' ) ;
25 }else if (S_ISDIR(fstat.st_mode)){
26 printf ( "%c" ,'d' ) ;
27 }else if (S_ISLNK(fstat.st_mode)){
28 printf ( "%c",'l') ;
29 }else if (S_ISCHR(fstat.st_mode)){
30 printf ( "%c",'c') ;
31 }else if (S_ISFIFO(fstat.st_mode)){
32 printf ( "%c",'p') ;
33 }else if (S_ISSOCK(fstat.st_mode)){
34 printf ( "%c",'s') ;
35 }else if (S_ISBLK(fstat.st_mode)){
36 printf ( "%c",'b') ;
37 }else{
38 //unknown type
39 printf ( "%c",'?') ;
40 }
41
42 //get file rwx
43 char *t1 = "rwxrwxrwx";
44 for (i=8; i>= 0; i--){
45 if (fstat.st_mode & (1 << i)) //print rwx
46 printf("%c", t1[8-i]);
47 else
48 printf("%c", '-');
49 }
50
51 printf("%4d " , fstat.st_nlink); // link count
52
53 //uid
54 struct passwd *user;
55 user = getpwuid(fstat.st_uid);
56 printf("%s\t",user->pw_name);
57
58 // gid
59 struct group *gdata;
60 gdata = getgrgid(fstat.st_gid);
61 printf("%s ", gdata->gr_name);
62
63 printf("%8d " , fstat.st_size) ; // file size
64 // print time
65 //strcpy(ftime,ctime(fstat.st_ctime)) ; //print time in calendar form
66 //ftime[strlen(ftime)-1] = 0; // kill in at end
67 //printf ("%s ",ftime); //print name
68 //printf ("%s",basename(fname)) ; // print file basename
69 struct tm *block;
70 block = localtime(&fstat.st_mtime);
71 printf("%4d-%02d-%02d %02d:%02d ",
72 block->tm_year+1900, block->tm_mon+1, block->tm_mday,
73 block->tm_hour, block->tm_min);
74
75 //print name
76 printf("%s",basename(fname));
77 // print -> linkname if symbolic file
78 if (S_ISLNK(fstat.st_mode)){
79 char buf[100];
80 int result = readlink(fname,buf,100-1);
81 if (result < 0){
82 perror("readlink ");
83 exit(1);
84 }
85 printf(" -> %s",buf); //print link file content
86 }
87 printf("\n");
88 }
89
90 int ls_dir(char *dname){
91 //use opendir( ), readdir( ) ; then ca11 1s_file(name)
92 DIR *dp;
93 struct dirent *sdp;
94 dp = opendir(dname);
95 if(dp==NULL){
96 perror("opendir failed ");
97 exit(1);
98 }
99 int len = strlen(dname);
100 while( (sdp = readdir(dp)) != NULL){
101 if(sdp->d_name[0]=='.'){
102 continue;
103 }
104 char filename[1024];
105 int i;
106 for(i=0;i<len;i++){
107 filename[i] = dname[i];
108 }
109 filename[len] = '/';
110 len++;
111 for(i=0;sdp->d_name[i]!='\0';i++){
112 filename[i+len]=sdp->d_name[i];
113 }
114 filename[i+len]='\0';
115 len--;
116 ls_file(filename);
117 }
118 closedir(dp);
119 }
120
121 int main (int argc, char *argv[]){
122 //get file status
123 struct stat sbuf;
124 int r;
125 char cwd[1024];
126 getcwd(cwd,1023);
127 char *route = strcat(cwd,"/");
128 if(argc == 2){
129 route = strcat(cwd,argv[1]);
130 }
131 r = lstat(route,&sbuf);
132 if(r<0){
133 perror("lstat ");
134 exit(1);
135 }
136
137 //
138 if(S_ISDIR(sbuf.st_mode)){
139 ls_dir(route);
140 }else{
141 ls_file(route);
142 }
143 return 0;
144 }
rm-r实现——remove_dir
1 #include <sys/stat.h>
2 #include <dirent.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include<sys/types.h>
7 #include <string.h>
8 #include <unistd.h>
9
10 int remove_dir(const char *dir)
11 {
12 char cur_dir[] = ".";
13 char up_dir[] = "..";
14 char dir_name[128];
15 DIR *pdir;
16 struct dirent *dp;
17 struct stat dir_stat;
18
19 // 参数传递进来的目录不存在,直接返回
20 if (access(dir, F_OK)==-1) {
21 return 0;
22 }
23
24 // 获取目录属性失败,返回错误
25 if (stat(dir, &dir_stat)<0) {
26 perror("get directory stat error");
27 return -1;
28 }
29
30 if (S_ISREG(dir_stat.st_mode) ) { // 普通文件直接删除
31 remove(dir);
32 } else if (S_ISDIR(dir_stat.st_mode) ) { // 目录文件,递归删除目录中内容
33 pdir = opendir(dir);
34 while ((dp=readdir(pdir)) != NULL ) {
35 // 忽略 . 和 ..
36 if ( (0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name)) ) {
37 continue;
38 }
39 sprintf(dir_name, "%s/%s", dir, dp->d_name);
40 remove_dir(dir_name); // 递归调用
41 }
42 closedir(pdir);
43 rmdir(dir); // 删除空目录
44 } else {
45 perror("unknow file type!");
46 exit(1);
47 }
48 }
49 int main(int argc,char *argv[]){
50 remove_dir(argv[1]);
51 return 0;
52 }
symlink
创建符号链接(软链接)
1 #include <unistd.h>
2
3 int main()
4 {
5 symlink("./myll.c","slink");
6 return 0;
7 }
实现cp-r
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#define BLOCK 4096
int ftype(char *path){
if(access(path,F_OK)==-1){
//文件不存在
return -1;
}else{
//文件存在
struct stat sbuf;
if(lstat(path,&sbuf) < 0){
perror("lstat ");
exit(1);
}
if(S_ISREG(sbuf.st_mode)){
return 1;
}else if(S_ISDIR(sbuf.st_mode)){
return 2;
}else if(S_ISLNK(sbuf.st_mode)){
return 3;
}
}
return 0;
}
void toslink(int fd){
//change reg file to soft link
}
int remove_dir(const char *dir)
{
char cur_dir[] = ".";
char up_dir[] = "..";
char dir_name[128];
DIR *pdir;
struct dirent *dp;
struct stat dir_stat;
// 参数传递进来的目录不存在,直接返回
if (access(dir, F_OK)==-1) {
return 0;
}
// 获取目录属性失败,返回错误
if (stat(dir, &dir_stat)<0) {
perror("get directory stat error");
return -1;
}
if (S_ISREG(dir_stat.st_mode) ) { // 普通文件直接删除
remove(dir);
} else if (S_ISDIR(dir_stat.st_mode) ) { // 目录文件,递归删除目录中内容
pdir = opendir(dir);
while ((dp=readdir(pdir)) != NULL ) {
// 忽略 . 和 ..
if ( (0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name)) ) {
continue;
}
sprintf(dir_name, "%s/%s", dir, dp->d_name);
remove_dir(dir_name); // 递归调用
}
closedir(pdir);
rmdir(dir); // 删除空目录
} else {
perror("unknow file type!");
exit(1);
}
return 1;
}
void file2file(char *src,char *des){
//打开src,只读
int fd1 = open(src,O_RDONLY);
if(fd1<0){
perror("open src file error");
exit(1);
}
//打开des,如果存在,截断;如果不存在,创建
int fd2 = open(des,O_WRONLY|O_CREAT|O_TRUNC,0644);
if(fd2<0){
perror("open dest file error");
exit(1);
}
//读src到buf,把buf写入des
char buf[BLOCK];
if(ftype(src)==3){ //复制软链接
int len = readlink(src,buf,sizeof(buf));
write(fd2,buf,len);
toslink(fd2);
}else{ //复制普通文件
int n;
while(n=read(fd1,buf,sizeof(buf))){
write(fd2,buf,n);
}
}
//关闭文件
close(fd1);
close(fd2);
}
void file2dir(char *src,char *des){
//改一下文件路径,在结尾加上src的文件名
char newpath[256];
sprintf(newpath,"%s/%s",des,basename(src));
//转变为file2file
file2file(src,newpath);
}
void dir2dir(char *src,char *des){
//打开目录src
DIR *dp1;
dp1 = opendir(src);
if(dp1==NULL){
perror("open src dir error");
exit(1);
}
//打开目录des
if(ftype(des)==-1){
//如果des不存在,创建
if(mkdir(des,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH)<0){
perror("mkdir failed");
exit(1);
}
}else if(ftype(des)==2){
//如果存在des,删除该目录,重新创建
remove_dir(des);
if(mkdir(des,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH)<0){
perror("mkdir failed");
exit(1);
}
}
struct dirent *srcdp;
//遍历src目录项
while((srcdp = readdir(dp1)) != NULL){
if(srcdp->d_name[0] == '.'){//跳过隐藏文件
continue;
}
//拼接目录项路径
char newsrc[256];
sprintf(newsrc,"%s/%s",src,srcdp->d_name);
char newdes[256];
sprintf(newdes,"%s/%s",des,srcdp->d_name);
if(ftype(newsrc)==2){ //如果目录项是目录
dir2dir(newsrc,newdes);
}else{ //如果目录项是文件
file2file(newsrc,newdes);
}
}
}
int main(int argc,char *argv[]){
//获取命令行参数,
//printf("argc = %d\n",argc);
if(argc!=3){
printf("命令格式错误\n");
exit(1);
}
char src[256],des[256];
char cwd[128];
getcwd(cwd,sizeof(cwd));
sprintf(src,"%s/%s",cwd,argv[1]);
sprintf(des,"%s/%s",cwd,argv[2]);
//如果src = des ,不用复制,直接退出
if(strcmp(src,des)==0){
exit(1);
}
//判断是哪一种,获取文件状态,
if(ftype(src)==2){ //src is dir
if(ftype(des)==2){
//目标目录存在,src成为其子文件夹
sprintf(des,"%s/%s",des,basename(src));
dir2dir(src,des);
}else if(ftype(des) == -1){
//目标目录不存在,创建目录
dir2dir(src,des);
}else{
printf("不能将目录复制到文件\n");
exit(1);
}
}else if(ftype(src)==1 || ftype(src)==3){ //src is file
if(ftype(des)==2){
file2dir(src,des);
}else{
file2file(src,des);
}
}
return 0;
}
新增
补充实现cp-r复制软链接(符号链接)
改进file2file函数
75 void file2file(char *src,char *des){
76 //复制软链接
77 char buf[BLOCK];
78 memset(buf,sizeof(buf),0);
79 if(ftype(src)==3){ //复制软链接
80 int len = readlink(src,buf,sizeof(buf));
81 buf[len]='\0'; //buf里面有多余的字符,00截断
82 symlink(buf,des);
83 return;
84 }
85 //打开src,只读
86 int fd1 = open(src,O_RDONLY);
87 if(fd1<0){
88 perror("open src file error");
89 exit(1);
90 }
91 //打开des,如果存在,截断;如果不存在,创建
92 int fd2 = open(des,O_WRONLY|O_CREAT|O_TRUNC,0644);
93 if(fd2<0){
94 perror("open dest file error");
95 exit(1);
96 }
97
98 int n;
99 while(n=read(fd1,buf,sizeof(buf))){
100 write(fd2,buf,n);
101 }
102 //关闭文件
103 close(fd1);
104 close(fd2);
105
106 }