赞
踩
首先了解如下文件存储相关概念:inode、 dentry、 数据存储、文件系统。
其本质为结构体,存储文件的属性信息。如:权限、类型、大小、时间、用户、盘块位置……也叫作文件属性管理结构,大多数的inode都存储在磁盘上。
少量常用、近期使用的inode会被缓存到内存中。
目录项,其本质依然是结构体,重要成员变量有两个 {文件名,inode,…},而文件内容(data)保存在磁盘盘块中。
dentry被删掉之后,inode还在,磁盘对应位置就不能被替换。说明文件并没有真正删除。
但是inode被删除以后,对应磁盘允许被替换,这样就会导致,文件真正可能被删除。
文件系统是,一组规则,规定对文件的存储及读取的一般方法。文件系统在磁盘格式化过程中指定。 常见的文件系统有:fat32 ntfs exfat ext2 、ext3 、ext4
获取文件属性,(从inode结构体中获取)
int stat(const char *path, struct stat *buf); 成返回0;失败返回-1 设置errno为恰当值。
参数1:文件名
参数2:inode结构体指针 (传出参数)
文件属性将通过传出参数返回给调用者。
练习:使用stat函数查看文件属性
【stat.c】
The stat structure
All of these system calls return a stat structure, which contains the following fields:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
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 ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
#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 sbuf;
int ret =stat(argv[1],&sbuf);
if(ret == -1){
perror("stat error");
exit(1);
}
printf("file size :%ld\n",sbuf.st_size);
return 0;
}
int lstat(const char *path, struct stat *buf);
成返回0;
失败返回-1 设置errno为恰当值。
练习:给定文件名,判断文件类型。 【get_file_type.c】
文件类型判断方法:st_mode 取高4位。 但应使用宏函数:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
穿透符号链接:stat:会;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 sbuf;
int ret =lstat(argv[1],&sbuf);
if(ret == -1){
perror("stat error");
exit(1);
}
if(S_ISREG(sbuf.st_mode)){
printf("it's a regular\n");
}else if(S_ISDIR(sbuf.st_mode)){
printf("it's a dir\n");
}else if(S_ISFIFO(sbuf.st_mode)){
printf("it's a pipe");
}else if(S_ISLNK(sbuf.st_mode)){
printf("it's a sym link\n");
}
return 0;
}
包含三个二进制位。依次是:设置组ID位setGID;设置用户ID位setID;黏住位sticky
早起计算机内存紧,只有精要的常用的程序可以常驻物理内存,剩下的要暂存磁盘中。当内存不够用的时候会将该部分程序存回磁盘,腾出内存空间。若文件设置了黏住位,那么即使在内存比较吃紧的情况下,也不会将该文件回存到磁盘上。由于现阶段操作系统的虚拟内存管理分页算法完善。该功能已经被废弃。
但我们仍然可以对目录设置黏住位。被设置了该位的目录,其内部文件只有:
①超级管理员
②该目录所有者
③该文件的所有者
以上三种用户有权限做删除、修改操作。其他用户可以读、创建但不能随意删除。
进程有两个ID:EID(有效用户ID),表示进程履行哪个用户的权限。
UID(实际用户ID),表示进程实际属于哪个用户。
多数情况下,EID和UID相同。但是,当文件的setID被设置后两个ID则有可能不一样。
例如:当进程执行一个root用户的文件,若该文件的setID位被设置为1, 那么执行该文件时,进程的UID不变。EID变为root,表示进程开始履行root用户权限。
setGID位于setID相类似。
测试指定文件是否存在/拥有某种权限。
int access(const char *pathname, int mode);
成功/具备该权限:0;
失败/不具备 -1 设置errno为相应值。
参数2:R_OK、W_OK、X_OK
通常使用access函数来测试某个文件是否存在。F_OK
修改文件的访问权限
int chmod(const char *path, mode_t mode);
成功:0;
失败:-1 设置errno为相应值
int fchmod(int fd, mode_t mode);
截断文件长度成指定长度。常用来拓展文件大小,代替lseek。
int truncate(const char *path, off_t length);
成功:0;
失败:-1 设置errno为相应值
int ftruncate(int fd, off_t length);
思考,为什么目录项要游离于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;
}
删除一个文件的目录项;
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;
}
~
~
当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。系统的这一特性称之为隐式回收系统资源。
创建一个符号链接
int symlink(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值
读取符号链接文件本身内容,得到链接所指向的文件名。
ssize_t readlink(const char *path, char *buf, size_t bufsiz); 成功:返回实际读到的字节数;
失败:-1
设置errno为相应值。
重命名一个文件。
int rename(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值
工作目录:“./”代表当前目录,指的是进程当前的工作目录,默认是进程所执行的程序所在的目录位置。
获取进程当前工作目录 (卷3,标库函数)
char *getcwd(char *buf, size_t size);
成功:buf中保存当前进程工作目录位置。
失败返回NULL。
改变当前进程的工作目录
int chdir(const char *path); 成功:0;失败:-1设置errno为相应值
练习:获取及修改当前进程的工作目录,并打印至屏幕。 【imp_cd.c】
注意:目录文件也是“文件”。其文件内容是该目录下所有子文件的目录项dentry。 可以尝试用vim打开一个目录。
目录设置黏住位:若有w权限,创建不变,删除、修改只能由root、目录所有者、文件所有者操作。
根据传入的目录名打开一个目录 (库函数) DIR * 类似于 FILE *
DIR *opendir(const char *name); 成功返回指向该目录结构体指针,失败返回NULL
参数支持相对路径、绝对路径两种方式:
例如:打开当前目录:
① getcwd() , opendir()
② opendir(".");
关闭打开的目录
int closedir(DIR *dirp); 成功:0;失败:-1设置errno为相应值
读取目录 (库函数)
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;
}
~
~
~
~
~
~
回卷目录读写位置至起始。
void rewinddir(DIR *dirp); 返回值:无。
获取目录读写位置
long telldir(DIR *dirp); 成功:与dirp相关的目录当前读写位置。失败-1,设置errno
修改目录读写位置
void seekdir(DIR *dirp, long loc); 返回值:无
参数loc一般由telldir函数的返回值来决定。
查询指定目录,递归列出目录中文件,同时显示文件大小。 【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;
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;
}
~
~
~
~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。