2.6 truncate函数
截断文件长度成指定长度。常用来拓展文件大小,代替lseek。 int truncate(const char *path, off_t length); 成功:0; 失败:-1 设置errno为相应值 int ftruncate(int fd, off_t length);
2.7 link函数
思考,为什么目录项要游离于inode之外,画蛇添足般的将文件名单独存储呢??这样的存储方式有什么样的好处呢? 其目的是为了实现文件共享。Linux允许多个目录项共享一个inode,即共享盘块(data)。不同文件名,在人类眼中将它理解成两个文件,但是在内核眼里是同一个文件。 link函数,可以为已经存在的文件创建目录项(硬链接)。 int link(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值 注意:由于两个参数可以使用“相对/绝对路径+文件名”的方式来指定,所以易出错。 如:link("../abc/a.c", "../ioc/b.c")若a.c,b.c都对, 但abc,ioc目录不存在也会失败。 mv命令既是修改了目录项,而并不修改文件本身。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> int main(int argc,char *argv[]) { link(argv[1],argv[2]); unlink(argv[1]); return 0; }
2.8 unlink函数
删除一个文件的目录项; int unlink(const char *pathname); 成功:0;失败:-1设置errno为相应值 练习:编程实现mv命令的改名操作 【imp_mv.c】 注意Linux下删除文件的机制:不断将st_nlink -1,直至减到0为止。无目录项对应的文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定) 因此,我们删除文件,从某种意义上说,只是让文件具备了被释放的条件。 unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放。要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。 【unlink_exe.c】
/* * unlink函数是删除一个dentry */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <fcntl.h> int main(void) { int fd,ret; char *p = "test of unlink\n"; char *p2 = "after write something.\n"; fd = open("temp.txt",O_RDWR|O_CREAT|O_TRUNC,0644); if(fd<0){ perror("open temp error"); exit(1); } ret = unlink("temp.txt"); if(ret<0){ perror("unlink error"); exit(1); } ret = write(fd,p,strlen(p)); if(ret == -1){ perror("----write error"); } printf("hi!I'm printf\n"); ret = write(fd,p2,strlen(p2)); if (ret == -1){ perror("----write error"); } printf("Enter anykey continue\n"); getchar(); close(fd); return 0; } ~ ~
2.9 隐式回收
当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。系统的这一特性称之为隐式回收系统资源。
2.10 symlink函数
创建一个符号链接 int symlink(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值
2.11 readlink函数
读取符号链接文件本身内容,得到链接所指向的文件名。 ssize_t readlink(const char *path, char *buf, size_t bufsiz); 成功:返回实际读到的字节数; 失败:-1 设置errno为相应值。
2.12 rename函数
重命名一个文件。 int rename(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值
3.目录操作
工作目录:“./”代表当前目录,指的是进程当前的工作目录,默认是进程所执行的程序所在的目录位置。
3.1 getcwd函数
获取进程当前工作目录 (卷3,标库函数) char *getcwd(char *buf, size_t size); 成功:buf中保存当前进程工作目录位置。 失败返回NULL。
3.2 chdir函数
改变当前进程的工作目录 int chdir(const char *path); 成功:0;失败:-1设置errno为相应值 练习:获取及修改当前进程的工作目录,并打印至屏幕。 【imp_cd.c】
3.3 文件、目录权限
注意:目录文件也是“文件”。其文件内容是该目录下所有子文件的目录项dentry。 可以尝试用vim打开一个目录。
目录设置黏住位:若有w权限,创建不变,删除、修改只能由root、目录所有者、文件所有者操作。
3.4 opendir函数
根据传入的目录名打开一个目录 (库函数) DIR * 类似于 FILE * DIR *opendir(const char *name); 成功返回指向该目录结构体指针,失败返回NULL 参数支持相对路径、绝对路径两种方式: 例如:打开当前目录: ① getcwd() , opendir() ② opendir(".");
3.5 closedir函数
关闭打开的目录 int closedir(DIR *dirp); 成功:0;失败:-1设置errno为相应值
3.6 readdir函数
读取目录 (库函数) struct dirent *readdir(DIR *dirp); 成功返回目录项结构体指针;失败返回NULL设置errno为相应值 需注意返回值,读取数据结束时也返回NULL值,所以应借助errno进一步加以区分。 struct 结构体: struct dirent { ino_t d_ino; inode编号 off_t d_off; unsigned short d_reclen; 文件名有效长度 unsigned char d_type; 类型(vim打开看到的类似@*/等) char d_name[256];文件名 }; 其成员变量重点记忆两个:d_ino、d_name。实际应用中只使用到d_name。 练习1:实现简单的ls功能。 练习2:实现ls不打印隐藏文件。每5个文件换一个行显示。 拓展1:实现ls -a -l 功能。 拓展2:统计目录及其子目录中的普通文件的个数
#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)) continue; printf("%s\t",sdp->d_name); } printf("\n"); closedir(dp); return 0; } ~ ~ ~ ~ ~ ~
3.7 rewinddir函数
回卷目录读写位置至起始。 void rewinddir(DIR *dirp); 返回值:无。
3.8 telldir/seekdir函数
获取目录读写位置 long telldir(DIR *dirp); 成功:与dirp相关的目录当前读写位置。失败-1,设置errno 修改目录读写位置 void seekdir(DIR *dirp, long loc); 返回值:无 参数loc一般由telldir函数的返回值来决定。
4.递归遍历目录
查询指定目录,递归列出目录中文件,同时显示文件大小。 【ls_R.c】
#include <dirent.h> 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); (*func)(path); } closedir(dp); return ; } void isfile(char *name) { int ret ; struct stat sb; ret = stat(name,&sb); if(ret == -1){ perror("stat error"); return; } if(S_ISDIR(sb.st_mode)){ read_dir(name,isfile); } else{ printf("%20s\t%ld\n",name,sb.st_size); } return;
5.重定向
dup 和 dup2函数 int dup(int oldfd); 成功:返回一个新文件描述符; 失败:-1设置errno为相应值 int dup2(int oldfd, int newfd);
重定向示 记忆方法两种: 1. 文件描述符的本质角度理解记忆。 2. 从函数原型及使用角度,反向记忆。 练习:借助dup函数编写mycat程序,实现cat file1 > file2 命令相似功能。
【mycat.c】 #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 fd = open(argv[1],O_RDONLY); int newfd = dup(fd); printf("newfd = %d\n",newfd); return 0; } ~ ~
#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); int fd2 = open(argv[2],O_RDWR); int fdret = dup2(fd1,fd2); printf("fdret = %d\n",fdret); int ret =write(fd2,"1234567",7); printf("ret = %d\n",ret); dup2(fd1,STDOUT_FILENO); printf("-------------------------886"); return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <pthread.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); printf("newfd = %d\n",newfd); int newfd2 = fcntl(fd1,F_DUPFD,7); printf("newfd2 = %d\n",newfd2); int ret = write(newfd2,"YYYYYY",7); printf("ret = %d\n",ret); return 0; } ~ ~ ~ ~