[C++系列] UNIX高级编程 day07 获取文件元数据,文件夹操作,文件锁
in C/C++ with 0 comment

[C++系列] UNIX高级编程 day07 获取文件元数据,文件夹操作,文件锁

in C/C++ with 0 comment

复习:

文件的操作

open close read write lseek

使用mmap将文件映射到内存的虚拟地址空间

文件重定向


获取文件元数据

文件有内容,文件内容的访问是通过read。write去做的。
除去内容之外,还有一些文件的描述信息。文件的类型,文件的权限,文件的大小,文件的创建时间。。。这些
数据就是文件的元数据

如何使用命令查看文件的元数据
ls -l
Stat filename
通过编写程序,来获取这些数据。

文件的内容和描述信息都是数据,都需要存储。如何存储?

在linux内核中,每个文件都且仅有一个对应的inode。一个inode可以对应多个文件名。如果一个inode对应多个文件的名字,那么拥有同一个inode的所有文件称为硬链接文件。

如何创建文件的硬链接文件

Ls -li filename 可以查看文件的元数据,带inode号。

软链接文件的内容是源文件的名字。通过文件的名字可以找到文件的inode。通过inode可以找到文件 的元数据和数据。
如果建立一个文件的软链接
ln -s hello h

如何获取一个文件的元数据

#include <sys/stat.h>
 int     stat(const char *restrict path, struct stat *restrict buf);

功能:获取文件的身份信息。
参数
path:指定文件的路径
Buf:是一个地址,存放文件身份信息的空间的首地址
返回值
成功:返回0
-1。 错误。errno被设置

struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */
         dev_t    st_dev;    /* device inode resides on */
         ino_t    st_ino;    /* inode's number */
         mode_t   st_mode;   /* inode protection mode */
         nlink_t  st_nlink;  /* number of hard links to the file */
         uid_t    st_uid;    /* user-id of owner */
         gid_t    st_gid;    /* group-id of owner */
         dev_t    st_rdev;   /* device type, for special file inode */
         struct timespec st_atimespec;  /* time of last access */
         struct timespec st_mtimespec;  /* time of last data modification */
         struct timespec st_ctimespec;  /* time of last file status change */
         off_t    st_size;   /* file size, in bytes */
         quad_t   st_blocks; /* blocks allocated for file */
         u_long   st_blksize;/* optimal file sys I/O ops blocksize */
         u_long   st_flags;  /* user defined flags for file */
         u_long   st_gen;    /* file generation number */
     };

练习

获取文件的元数据:

1 #include<stdio.h>
  2 #include<sys/stat.h>
  3 #include<sys/types.h>
  4 #include <fcntl.h>
  5 #include <unistd.h>
  6 int main(int argc,char *argv[]){
  7         struct stat sbuf;
  8         //获取文件的元数据
  9         int s = stat(argv[1],&sbuf);
10         if(s==-1){
11                 perror("stat");
12                 return -1;
13         }
14         //执行到这里的时候,已经获取到了文件的元数据。
15         printf("hard links:%lu\n",sbuf.st_mode);
16
17         return 0;
18 }
~```

系统中记录的`时间戳`,但是生活中需要字符串。
需要将时间戳转换为字符串。使用` ctime(2)`转换。

include<time.h>

char ctime(const time_t clock);

**功能**:将时间戳转换为字符串格式。
**参数**:
timep:指定存放时间戳的地址。
**返回值**:
NULL 错误。  
返回字符串的首地址

返回字符串格式
` Nov 24 18:22:48 1986\n\0`
`#include<time.h>`
`printf("access time:%s",ctime(&sbuf.st_atimespec));`

文件的拥有者:
linux系统是多用户,多任务的操作系统
系统需要记录这些用户信息。
记录在`/etc/passwd `  文件夹下
`root:*:0:0:System Administrator:/var/root:/bin/sh `
以冒号分割了7列
* 第一列:  用户的名字
* 第二列:登陆的时候,是否需要密码
* 第三列:用户的id,uid
* 第四列:用户所属组的id,gid
* 第五列:用户的注释信息
* 第六列:用户的工作组目录(家目录)
* 第七列:用户登录后执行的第一个程序。用户和系统交互的程序

用户看到的是用户名,但系统维护一个数字。
需要将uid转换为字符串:
`getpwuid()`函数

include <sys/types.h>

 #include <pwd.h>
 #include <uuid/uuid.h>

struct passwd *    getpwent(uid_t uid);
**功能**:获取获取`passwd`文件中的一条信息。
参数:
uid :指定用户的uid
**返回值**:
返回指定一个条目的地址。
`NULL`    没有找到匹配的条目,或者错误发生。如果发生错误,`errno`被设置。

struct passwd {

               char    *pw_name;       /* user name */
               char    *pw_passwd;     /* encrypted password */
               uid_t   pw_uid;         /* user uid */
               gid_t   pw_gid;         /* user gid */
               time_t  pw_change;      /* password change time */
               char    *pw_class;      /* user access class */
               char    *pw_gecos;      /* Honeywell login info */
               char    *pw_dir;        /* home directory */
               char    *pw_shell;      /* default shell */
               time_t  pw_expire;      /* account expiration */
               int     pw_fields;      /* internal: fields filled in */
       };

1 #include<stdio.h>
2 #include<sys/stat.h>
3 #include<sys/types.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include<time.h>
7 #include <pwd.h>
8 #include <uuid/uuid.h>
9 #include<grp.h>
10 int main(int argc,char *argv[]){
11 struct stat sbuf;
12 //获取文件的元数据
13 int s = stat(argv[1],&sbuf);
14 if(s==-1){
15 perror("stat");
16 return -1;
17 }
18 //执行到这里的时候,已经获取到了文件的元数据。
19 printf("hard links:%dn",sbuf.st_mode);
20 printf("access time:%dn",sbuf.st_atimespec);
21 printf("access time:%s",ctime(&sbuf.st_atimespec));
22 printf("uid%un",sbuf.st_uid);
23 struct passwd *pss = getpwuid(sbuf.st_uid);
24 printf("username : %sn",pss->pw_name);
25 struct group *gp = getgrgid(sbuf.st_gid);
26 printf("group name:%sn",gp->gr_name);
27 return 0;
28 }
~

1 #include<stdio.h>
2 #include<sys/stat.h>
3 #include<sys/types.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include<time.h>
7 #include <pwd.h>
8 #include <uuid/uuid.h>
9 #include<grp.h>
10 int main(int argc,char *argv[]){
11 struct stat sbuf;
12 //获取文件的元数据
13 int s = stat(argv[1],&sbuf);
14 if(s==-1){
15 perror("stat");
16 return -1;
17 }
18 //执行到这里的时候,已经获取到了文件的元数据。
19 printf("hard links:%dn",sbuf.st_mode);
20 printf("access time:%dn",sbuf.st_atimespec);
21 printf("access time:%s",ctime(&sbuf.st_atimespec));
22 printf("uid%un",sbuf.st_uid);
23 struct passwd *pss = getpwuid(sbuf.st_uid);
24 printf("username : %sn",pss->pw_name);
25 struct group *gp = getgrgid(sbuf.st_gid);
26 printf("group name:%sn",gp->gr_name);
27 printf("mode %on",sbuf.st_mode);
28 unsigned int m=sbuf.st_mode;

        /*

30 if(S_ISREG(m))printf("-n");
31 if(S_ISDIR(m)) printf("dn");
32 */
33 switch(m&S_IFMT){
34 case S_IFREG:
35 printf("-");
36 break;
37 case S_IFDIR:
38 printf("d");
39 break;
40 default:
41 printf("n");
42
43 }
44 //获取文件拥有者的权限。
45 mode_t om = m&S_IRWXU;
46 //检测拥有者是否有读取的权限
47 if(om&S_IRUSR) printf("r");
48 else printf("-");
49 if(om&S_IWUSR) printf("w");
50 return 0;
51 }

文件的组:
通过组id找到组名
组的信息存放在`/etc/group `   
`Root:x:0:10`
`Drop:x:1000`
四列:
通过组id获取组名:
`getgrgid`

include <grp.h>

include <uuid/uuid.h>

struct group * getgrgid(gid_t gid);

**功能**:从组文件中获取一条信息。
**参数**:
gid:组号
**返回值**:
返回一个指向结构体的指针。
NULL。错误或失败,如果失败,`errno`被设置

struct group {

               char    *gr_name;       /* group name */
               char    *gr_passwd;     /* group password */
               gid_t   gr_gid;         /* group id */
               char    **gr_mem;       /* group members */
       };
`St_mode `   成员不仅包含文件的权限,还包含文件的类型。


## 文件夹的操作

文件夹的读写。
什么是文件夹的数据:
文件夹的数据就是文件夹里的内容或者文件夹

`drwxr-xr-x  2 drop  staff    68  9  5 14:28 test`

`0777`

需要访问文件夹里的内容。
`Opendir(3)`
`Reader(3)`
`Closed(3)`
 #include <dirent.h>

 DIR *     opendir(const char *filename);
**功能**:打开文件夹,并将位置定位到文件夹的第一条数据上
**参数**:
name:指定了要打开的文件夹的名字
**返回值**:
`NULL`  错误 `errno`被设置
返回一个指向文件夹流的指针


`int     closedir(DIR *dirp);`
**功能**:关闭一个文件夹流
**参数**:
dirp:指定要关闭的文件夹流。
**返回值**:
0。   成功
-1     错误    errno被设置

include <dirent.h>

struct dirent readdir(DIR dirp);


**功能**:获取文件夹流中的一条内容
**参数**:
Dirp:指定要读取的文件夹流。
**返回值**:
0 成功;
-1 错误。errno被设置

struct dirent
  {
  long d_ino; / inode number 索引节点号 /
  off_t d_off; / offset to this dirent 在目录文件中的偏移 /
  unsigned short d_reclen; / length of this d_name 文件名长 /
  unsigned char d_type; / the type of d_name 文件类型 /
  char d_name [NAME_MAX+1]; / file name (null-terminated) 文件名,最长255字符 /
  }


## 练习:

文件夹的打开和关闭

1 #include<stdio.h>
2 #include<sys/types.h>
3 #include <dirent.h>
4 int main(int argc,char *argv[]){
5 DIR *dir;
6 //打开文件夹
7 dir = opendir(argv[1]);
8 if(!dir){
9 perror("opendir");
10 return -1;
11 }
12 printf("success");
13 //关闭文件夹流
14 closedir(dir);
15 return 0;
16 }

    读取文件夹:

1 #include<stdio.h>
2 #include<sys/types.h>
3 #include <dirent.h>
4 int main(int argc,char *argv[]){
5 DIR *dir;
6 struct dirent *dt;
7 //打开文件夹
8 dir = opendir(argv[1]);
9 if(!dir){
10 perror("opendir");
11 return -1;
12 }
13 printf("okn");
14 dt = readdir(dir);
15 //将指定文件夹下的内容打印出来
16 while((dt=readdir(dir)))
17 printf("%st",dt->d_name);
18
19 //关闭文件夹流
20 closedir(dir);
21 return 0;
22 }

##  作业

可以结合文件夹的操作和获取文件的元数据。`rls`
`rls -l filename`

## 文件锁的使用

多进程对文件的安全操作
* 1,加锁
* 2,访问文件内容
* 3,解锁


对文件的锁分为两种类型        
* 读锁(共享锁)
* 写锁 (互斥锁)

文件锁的实现两种方法:

* 建议锁:
* 强制锁:

如果在程序中使用建议锁:

使用`fcntl(2)`实现对文件的加锁和解锁。

include <fcntl.h>

int fcntl(int fildes, int cmd, ...);

**功能**:操作文件描述符
**参数**:
Fd:指定了文件描述符
Cmd:指定了对文件描述符的具体操作命令

`…`:可变参数。参数有没有,有几个,类型都取决于cmd
返回值:
成功: 0
失败:-1。errno被设置

建议锁的`cmd`
`F_GETLK`:测试是否可以放置锁。如果可以放置,锁的类型是F_UNLCK。如果不能放置,可以
获取到持有互斥锁的进程的pid。
`F_SETLK`:获取或释放一把锁,如果有其它进程占用着,还是互斥锁,立即返回。返回错误, errno被设置
`F_SETLKW`:类似`F_SETLK`的操作,如果有其它进程占用,阻塞等待锁的释放。
第三个参数的结构体

struct flock {

             off_t       l_start;    /* starting offset */
             off_t       l_len;      /* len = 0 means until end of file */
             pid_t       l_pid;      /* lock owner */
             short       l_type;     /* lock type: read/write, etc. */
             short       l_whence;   /* type of l_start */
         };
使用`close(2)`后所有文件描述符上的锁被释放。

## 练习

多进程使用文件锁对文件进行访问。

include<stdio.h>

11 int main(int argc,char *argv[]){
12 int fd;
13 struct flock lock;
14 //打开文件
15 fd= open(argv[1],O_RDONLY);
16 if(fd==-1){
17 perror("open");
18 return -1;
19 }
20 lock.l_type=F_RDLOCK:
21 lock.l_whence=SEEK_SET;
22 lock.l_start=0;
23 lock.l_len=6;
24 //加锁
25 int f = fcntl(fd,F_SETLK,&lock);
26 if(f==-1){
27 perror("fcntl");
28 return -1;
29 }
30 getchar();
31 //关闭的时候文件描述符上的所有的锁释放了
32 close(fd);
33 return 0;

## 总结
* 1,获取文件的元数据
* 2,文件夹的操作
* 3,文件 锁    





Responses