2. 文件和目录
2.1 文件类型
文件类型主要有下面几种
(1)普通文件
(2)目录文件
(3)块特殊文件
提供对设备带缓冲的访问,每次访问固定长度
(4)字符特殊文件
提供对设备不带缓冲的访问,每次访问非固定长度
(5)FIFO
管道
(6)套接字
用于网络间通信
(7)符号链接
软链接,里面存放的是指向的文件路径
访问文件信息的主要函数是下面几个,主要的操作方式是从文件指向的inode中提取出需要的信息来写入一个struct stat结构
struct stat{
mode_t st_mode; //文件类型&文件访问权限
...
nlink_t st_nlink; //(硬)链接的数量
uid_t st_uid; //用户id
gid_t st_gid; //组id
off_t st_size; //文件的大小
...
}
//出错返回-1,否则返回0
int stat(const char* pathname,struct stat* buf)
int fstat(int fd,struct stat* buf)
以上两个函数是会跟踪符号链接的
int lstat(const char* pathname,struct stat* buf)
这个函数不跟踪符号链接
int fstatat(int fd,const char* pathname,struct stat* buf,int flag)
若path为绝对路径,则fd失效,否则fd为指定目录,fd为AT_FDCWD时为当前进程的工作目录
flag:AT_SYMLINK_NOFOLLOW标志设置的话则不会跟随标志
使用一些宏可以确定文件类型,对stat结构中的st_mode进行判断的宏主要有
宏 | 文件类型 |
---|---|
S_ISREG() | 普通文件 |
S_ISDIR() | 目录文件 |
S_ISCHR() | 字符特殊文件 |
S_ISBLK() | 块特殊文件 |
S_ISFIFO() | 管道或者FIFO |
S_ISLNK() | 符号链接 |
S_ISSOCK() | 套接字 |
同时,一些IPC也可以表示为文件,对其的判断直接对stat的指针操作
宏 | 对象的类型 |
---|---|
S_TYPEISMQ() | 消息队列 |
S_TYPEISSEM() | 信号量 |
S_TYPEISSHM() | 共享存储对象 |
2.2 文件访问权限
一个文件的访问权限分为root用户权限,组访问权限,当前用户访问权限
通常,读权限表示为4,写为2,执行为1
rwx=7,rw=6,x=1,通过并的方式来给出文件访问权限
注意,新的文件创建的时候,系统会根据当前的文件模式创建屏蔽字来屏蔽相关的文件权限,即根据umask值来取反并与。
st_mode中也包含了文件的访问权限,可以对它&上一个相关的权限来判断是否拥有该权限
还有一些规则是需要注意的
(1)打开任意类型的文件时,对必须对它的祖先目录具有执行的权限
(2)在一个目录中创建一个文件,必须对该文件具有写权限和执行权限
(3)删除一个现有文件,必须对包含该文件的目录具有写权限和执行权限,对该文件本身并不需要有读写权限。
(4)创建的新的文件一般情况下是拥有进程的相关权限
mode_t uamsk(mode_t cmask)
返回值:之前的文件模式创建屏蔽字
cmask:一般是由若干的文件权限字‘或’起来
int chmod(const char* pathname,mode_t mode)
int fchmod(int fd,mode_t mode)
int fchmodat(int fd,const char *pathname,mode_t mode,int flag)
返回值:成功则返回0,否则返回-1
fd: AT_FDCWD
flag: AT_SYMLINK_NOFOLLOW
int chown(const char* pathname,uid_t owner,gid_t group)
int fchown(int fd,uid_t owner,gid_t group)
上面两个跟随符号链接
int fchownat(int fd,const char* pathname,uid_t owner,gid_t group,int flag)
fd: AT_FDCWD
flag: AT_SYMLINK_NOFOLLOW
int lchown(const char* pathname,uid_t owner,gir_t group)
会修改符号链接
2.3 文件系统
目录项->inode->块
删除一个文件使用的是unlink,即删除一个目录项,只有当指向这个文件的目录项全部删除,才会彻底删除这个文件,即st_nlink为0时
硬链接:一个目录项指向inode这种称为硬链接
软链接(符号链接):类似于快捷方式,里面实际存放的时一个文件路径
对于st_size,对于非符号链接,实际表明的是一个文件的大小,而对于符号链接,则包含的时指向的文件的路径的长度,即"文件名"的长度
任何一个文件可以有多个目录项指向其i节点,创建一个指向现有文件的链接(硬)的方法如下
int link(const char* existpath,const char* newpath )
int linkat(int efd,const char* existpath,int nfd,const char* newpath,int flag)
efd,nfd: AT_FDCWD
flag: AT_SYMLINK_FOLLOW
符号链接跟随,即新建立一个链接指向(老的符号链接指向的文件)
//删除一个硬链接,如果文件名为一个符号链接,则不跟随符号链接!
int unlink(const char* pathname)
当文件计数为0时,则彻底删除这个文件(不可以为目录)
int unlinkat(int fd,const char* pathname,int flag)
fd: AT_FDCWD
flag: AT_REMOVEDIR 可删除目录,但是必须为空目录(带. ..)
//可以删除文件和目录都可以,但是目录必须为空
int remove(const char* pathname)
unlink只有在文件计数为0时并且没有进程打开这个文件的时候才会立即删除这个文件,否则会等待到进程结束的时候再删除
利用这种特性,可以创建临时文件,比如creat一个文件之后再unlink它,然后可以保证如果进程崩溃也不会留下这个文件
至于文件的重命名操作,一般是新建一个目录项,再指向inode节点,然后删除那个目录项(老)
int rename(const char* oldname,const char* newname)
int renameat(int oldfd,const char *oldname,int newfd,const char* newname)
oldname:
文件:
newname不能为目录,并且如果newname重名,则删除重名的那新建一个出来
目录:
newname必须为一个目录,并且如果newname重名,则重名的那个只有为空才可以删除,同时newname不可以包含老的oldname为祖先路径
符号链接不跟随!!!
2.4 符号链接
硬链接要求必须在同一个文件系统中
符号链接不要求在同一个文件系统中
只有超级用户才能创建指向目录的硬链接,但是任何用户都可以创建指向目录的符号链接
创建和读取符号链接的函数如下
//创建符号链接
int symlink(const char *actualpath,const char* sympath)
并不要求actualpath已经存在,并且不要求在同一个文件系统中
int symlinkat(const char* actualpath,int fd,const char* sympath)
fd: AT_FDCWD
//读取符号链接(open会跟随,所以不可以!)
ssize_t readlink(const char* pathname,char* buf,size_t bufsize)
ssize_t readlinkat(int fd,const char* pathname,char* buf,size_t bufsize)
成功则返回读取的字节数,注意读入buf的内容不以NULL结尾!
2.5 目录
创建删除目录的函数如下
int mkdir(const char* pathname,mode_t mode)
int mkdirat(int fd,const char* pathname,mode_t mode)
创建一个新的空目录,会自动创建.和..,文件访问权限会由进程的文件模式创建屏蔽字修改
int rmdir(const char* pathname) //删除空目录
链接计数为0并且无进程打开才可以彻底删除,否则若有进程打开则先删除其.和..,并在此目录中无法创建文件,否则会出问题!
打开和读取目录的函数如下
DIR* opendir(const char *pathname)
DIR* fdopendir(int fd)
返回一个”目录描述符“,注意fd中的偏移量会起作用
struct dirent{
...
char d_name[];
}
struct dirent* readdir(DIR* dp)
读取目录中的一个目录项,主要用到的还是dirent结构中的d_name[]文件名
更改当前的工作目录的操作如下
int chdir(const char* pathname)
int fchdir(int fd)
//获得当前的工作目录
char* getcwd(char *buf,size_t size)
其实是递归的调用读取..,找到..中的目录项中与自己的inode相同的目录项