获取文件的元数据
在前面的学习中,对文件的操作,都是针对于文件的内容。
文件的属性和权限称为文件的元数据。这些内容和文件的内容是分开存放的。
系统提供了下述函数用于获取一个文件的元数据:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
功能:获取文件的元数据
参数:
pathname:指定文件的路径
buf:将文件的元数据填充到buf指定的空间里
返回值:
success:返回0
error:返回-1,设置errno
struct stat {
dev_t st_dev; /* ID of device containing file 设备的ID*/
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection 权限*/
nlink_t st_nlink; /* number of hard links 硬链接数*/
uid_t st_uid; /* user ID of owner 拥有者用户的ID*/
gid_t st_gid; /* group ID of owner 拥有组的ID*/
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes 文件的大小*/
blksize_t st_blksize; /* blocksize 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
};
注意stat结构体,函数stat(2)第二个参数是一个结构体的地址。它接受stat(2)函数返回的内容。
stat结构体中的成员描述了一个文件的元数据。
可以看到,再stat结构体中,返回的是文件属主的UID和文件属组的GID,仅仅是一个数字,系统提供了下列函数,可以通过UID和GID来获取属主和数组的其他信息(在linux中,用户属性的记录文件是/etc/passwd,组属性的记录文件是/etc/group):
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
功能:从passwd文件中获取一条记录
参数:
uid:指定要找的UID
返回值:
成功:返回有效地址
error:NULL ,没找到,或者错误,如果是错误,errno被设置
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
#include <sys/types.h>
#include <grp.h>
struct group *getgrgid(gid_t gid);
功能:返回组文件中的一条记录
参数:
gid:指定的gid的值
返回值:
成功
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* NULL-terminated array of pointers
to names of group members */
};
NULL 或者出错,出错的时候会设置errno的值
在结构体stat的成员变量中,访问时间返回的是一个长整型的变量,这种格式不利于阅读。系统提供了以下函数,可以将时间转换成易读的字符串格式:
#include <time.h>
char *ctime(const time_t *timep);
下面通过一个例子,来打印一个文件的属性:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <stdlib.h>
#include <grp.h>
#include <time.h>
int main(int argc,char *argv[]){
struct stat buf = {0};
int flag = stat(argv[1],&buf);
if(flag == -1){
perror("stat error");
return -1;
}
//解析buf中的每一个成员
printf("inode number:%lu\n",buf.st_ino);
switch(buf.st_mode & S_IFMT){
case S_IFSOCK :
printf("s");
break;
case S_IFREG :
printf("-");
break;
default :
break;
}
(buf.st_mode & S_IRUSR) ?printf("r"):printf("-");
(buf.st_mode & S_IWUSR) ?printf("w"):printf("-");
(buf.st_mode & S_IXUSR) ?printf("x"):printf("-");
(buf.st_mode & S_IRGRP) ?printf("r"):printf("-");
(buf.st_mode & S_IWGRP) ?printf("w"):printf("-");
(buf.st_mode & S_IXGRP) ?printf("x"):printf("-");
(buf.st_mode & S_IROTH) ?printf("r"):printf("-");
(buf.st_mode & S_IWOTH) ?printf("w"):printf("-");
(buf.st_mode & S_IXOTH) ?printf("x "):printf("- ");
printf("%d ",buf.st_nlink);
printf("%s ",getpwuid(buf.st_uid) -> pw_name);
printf("%s ",getgrgid(buf.st_gid) -> gr_name);
printf("%lu ",buf.st_size);
printf("%s ",ctime(&buf.st_ctime));
return 0;
}
文件夹的操作
文件夹的内容包括:文件和文件夹。
文件夹的操作权限和一般文件代表的意思不同。
r:能否读取这个文件夹下的文件/文件夹(但是!如果具有该文件夹下的文件/文件夹的操作权限,是可以操作的,只是说对当前的文件夹没有可以阅读其内容的权限)
w:能否增加和减少这个文件夹下的文件/文件夹的数量
x:决定能否通过这个文件夹
如果要直观的替换上述三个权限的情况,可以使用chmod来改变一个文件夹的权限,然后用指令去验证。
系统提供了以下函数,用于操作文件夹:
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开一个文件夹
参数:
name:指定要打开的文件夹,位置定位在文件夹的首条记录上
返回值:
error:NULL。errno被设置
success:返回一个地址
DIR对于我们来说是透明的
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
功能:关闭一个文件夹
参数:
dirp:opendir的返回值,要关闭的文件夹流
返回值:
success:返回0
error:返回 -1,errno被设置
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
功能:
参数:
dirp:opendir的返回值,指定了文件夹流中读取数据
返回值:
NULL,到达了文件夹的末尾,errno不会被设置;
或者错误,errno被设置
成功,返回如下结构体的地址
struct dirent
{
__ino_t d_ino;
__off_t d_off;
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256];
};
下面通过一个程序来演示上述函数的使用:
#include<stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc,char *argv[]){
//通过argv打开一个文件夹
DIR *p_dir = opendir(argv[1]);
if(p_dir == NULL){
perror("opendir is error");
return -1;
}
//打开文件夹成功
printf("opendir is success!\n");
struct dirent *p_rdir = NULL;
while((p_rdir = readdir(p_dir)) != NULL){
printf("name:%s......inode:%lu\n"\
,p_rdir -> d_name,p_rdir -> d_ino);
}//每次读取文件夹中的内容的时候,都会导致位置指针移动,所以可以循环读取,直到文件夹中的最后一个成员
//关闭文件夹
closedir(p_dir);
return 0;
}
在读取文件夹内容的时候,和读取文件的内容是类似的,文件夹的位置指针指向下一个要读取的文件/文件夹。每次读取之后,位置指针都会移动到相应的位置。
readdir(2)通过值-结果的形式,将描述文件/文件夹情况的结构体返回。
文件锁的使用
当两个进程同时读写一个文件的时候,会出现不可预知的情况。
为了保证进程读写文件和文件内容的有效性,操作系统提供了文件锁机制。
当一个进程对文件访问的时候,对该文件上锁,其他进程对该文件访问的时候要先获取文件锁的类型,看看能否对文件进行访问。
操作系统,提供了两种锁的机制,建议锁和强制锁。
下述内容,对建议锁进行详细的解读。
锁分为读锁(共享锁)、写锁(互斥锁)
当一个文件上有一把写锁的时候,其他的进程时无法访问这个文件。
系统提供了函数用于操作文件锁:
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
功能:对文件描述符的操作
参数:
fd:要操作的文件描述符
cmd:对文件描述符的操作命令
一大堆
建议锁命令:
F_SETLK
F_SETLKW
F_GETLK
...:可变参数,取决于cmd
建议锁需要的参数:
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(set by F_GETLK and F_OFD_GETLK) */
...
};
返回值:
success:返回 0
error:-1,errno被设置
下面用一个例子来演示上述函数:
/*pa*/
#include<stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[]){
struct flock lock;
int fd = open(argv[1],O_RDWR);
if(fd == -1){
perror("open file error");
return -1;
}
printf("open file success\n");
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 6;
int flag_fcntl = fcntl(fd,F_SETLK,&lock);
if(flag_fcntl == -1){
perror("test lock error\n");
return -1;
}
printf("write lock success");
getchar();
close(fd);
return 0;
}
------------------------------------------------
/*pb*/
#include<stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[]){
struct flock lock;
int fd = open(argv[1],O_RDWR);
if(fd == -1){
perror("open file error");
return -1;
}
printf("open file success\n");
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 6;
int flag_fcntl = fcntl(fd,F_SETLK,&lock);
if(flag_fcntl == -1){
perror("test lock error\n");
return -1;
}
close(fd);
return 0;
}
------------------------------------------------
/*pc*/
#include<stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[]){
struct flock lock;
int fd = open(argv[1],O_RDWR);
if(fd == -1){
perror("open file error");
return -1;
}
printf("open file success\n");
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 6;
int flag_fcntl = fcntl(fd,F_GETLK,&lock);
if(flag_fcntl == -1){
perror("test lock error\n");
return -1;
}
if(lock.l_type == F_UNLCK){
printf("can lock\n");
}
else{
printf("flock's pid:%d\n",lock.l_pid);
}
close(fd);
return 0;
}
pa函数,向文件加一把读锁;
pb函数,向文件加一把写锁;
pc函数,获取文件上的锁,询问是否可以加写锁;