unix环境高级编程第四章笔记

文件和目录

start fstart lstart函数

一旦给出pathname, start函数就返回了与此命名文件有关的信息结构

  #include <sys/start>
  int stat(const char *pathname, struct stat *buf);
  int fstat(int fd, struct stat *buf);
  int lstat(const char *pathname, struct stat *buf);/*能观察到符号链接*/

参数:

pathname:文件路径
fd:文件描述符
buf:存放每天与此命名文件有关信息结构的地址

返回值:

成功返回0 失败返回1

   struct stat {
        mode_t               st_mode;       // 文件类型和访问权限
        ino_t                st_ino;        // 指向数据块的i节点的编号
        dev_t                st_dev;        // 设备号
        dev_t                st_rdev;                
        nlink_t              st_nlink;      // 硬链接数
        uid_t                st_uid;        // 用户ID
        gid_t                st_gid;        // 用户组ID
        off_t                st_size;
        struct timespec      st_atim;       // 数据访问时间
        struct timespec      st_mtim;       // 数据修改时间
        struct timespec      st_ctim;       // 属性修改时间
        blksize_t            st_blksize;    // 合适的IO块大小
        blkcnt_t             st_blocks;     //分配了多少block
   };

文件类型

普通文件(regular file):包含数据的常规文件,数据可以是文本类型的,也可以是二进制的。

目录文件(directory file):它是一个目录,保存目录相关的信息。

块设备文件(block special file):这种文件提供对硬件(如磁盘)带缓冲的访问。

字符设备文件(regular file):这种文件提供对硬件(如磁盘)不带缓冲的访问。

FIFO:这种文件用于进程间通信。有时也被称为命名管道。

套接字文件(regular file):这种文件用于网络间通信。也可以用在一台宿主机进程上的非网络通信。

符号连接(regular file):类似Windows系统的快捷方式,指向另外一个文件。

文件类型的信息包含在start中的st_mode中,可以用以下的宏函数来判断文件类型。

  S_ISREG()        /* 普通文件 */
  S_ISDIR()        /* 目录 */
  S_ISCHR()        /* 字符文件 */
  S_ISBLK()        /* 块文件 */
  S_ISFIFO()       /* 管道文件 */
  S_ISLINK()       /* 链接文件 */
  S_ISSOCK()       /* 网络套接字 */

如果要确定IPC对象的类型用以下宏函数,但是参数改为指向start结构体的指针。

  S_TYPEISMQ()      /*消息队列*/
  S_TYPEISSEM()     /*信号量*/
  S_TYPEISSHM()     /*共享存储对象*/

简单用法:

  #include "apue.h"
  int
  main(int argc, char *argv[])
  {
      int i;
      struct stat buf;
      char *ptr;

      for (i = 1;i < argc; i++) {
	      printf("%s: ", argv[i]);
	      if (lstat(argv[i], &buf) < 0) {
		      err_ret("lstat error");
		      continue;
	      }
	      if (S_ISREG(buf.st_mode))
		      ptr = "regular";
	      else if (S_ISDIR(buf.st_mode))
	      	      ptr = "directory";
	      else if (S_ISCHR(buf.st_mode))
		      ptr = "character special";
	      else if (S_ISBLK(buf.st_mode))
		      ptr = "block special";
	      else if (S_ISFIFO(buf.st_mode))
		      ptr = "fifo";
	      else if (S_ISLNK(buf.st_mode))
		      ptr = "symbolic link";
	      else if (S_ISSOCK(buf.st_mode))
		      ptr = "socket";
	      else
		      ptr = "** unknown mode **";
	            printf("%s\n", ptr);
      }
      exit(0);
  }

输入:./a.out pathname1 pathname2

文件访问权限

所有文件类型都有文件访问权限,每个文件有九个文件访问权限位,可将其分为三类。分别表示用户u 组g 其他o的读写执行权限

我们用名字来打开任意类型的文件时,对该名字所包含的每一个目录包括当前目录,都必须具有执行权限

读权限用于目录可以读取目录包含的文件,例如ls,对以一个普通文件的读权限决定了我们打开文件后能否进行读操作

在目录中创建一个文件,必须对该目录具有写权限和执行权限,在目录中删除一个文件时不需要对该文件的读写权限

想要打开一个文件必须对该文件具有可执行权限,想要对该文件修改内容(对目录来说是创建或者删除文件),必须在打开该文件

或目录的基础上(具有执行权限)具有写权限才可以,而想要读取文件则只需要对该文件有执行权限和读权限。

进程每次操作一个文件时,内核就会进行文件权限测试,即把文件的所有者(st_uid st_gid)和进程的有效ID(有效用户ID和有效组ID)对比

  • 如果进程的有效用户id为0(超级用户)则允许访问
  • 进程的有效ID等于文件所有者的ID(进程拥有这个文件),如果文件所有者适当的访问权限被设置(读写执行权限)则允许访问
  • 进程有效组ID等于文件的组ID,判断方法同上
  • 其他用户适当的访问权限被设置,也允许访问

新文件和目录的所有权

创建新文件时,新文件的组id可以是进程的有效组id也可以是他所在目录的id,取决于设置

access函数

判断当前用户是否具有以某种模式访问某个文件的权限

  #include<unistd.h>
  int access(const char *pathname, int mode);

参数:

mode可取值如下R_OK、W_OK、X_OK、F_OK(测试文件是否存在)

返回值:

能访问则返回0,不能访问则返回-1

umask函数

umask为进程创建新文件时对文件模式创建屏蔽字,修改文件权限时没有效果,并返回以前的值

  #include<sys_start.h>
  mode_t umask(mode_t cmask);

返回值:

以前的文件模式屏蔽字

  /* 例子 */
  mode_t mode = umask(0111);    
  if(creat("a.txt",0777)<0)/* 原来设置的权限是0777,那么最终的结果是0777 - 0111 = 0666*/
  err_sys("creat error");
  umask(mode);    /* 恢复修改之前的umask数值 */

chmod和fchmod函数

可以更改现有文件的访问权限 chmod修改指定文件的访问权限,fchmod修改已经打开的文件的访问权限(打开文件返回文件描述符)

  #include <sys_start.h>
  int chmod(const char* pathname ,mode_t mode);
  int fchmod(int fd,mode_t mode);

返回值:

修改成功返回0,失败返回-1

想要修改一个文件的权限位,进程的有效id必须等于文件所有者的id,或者该进程拥有超级用户权限

mode 说明
S_ISUID 执行时设置用户id
S_ISGID 执行时设置组id
S_ISVTX 保存正文
---- ----
S_IRWXU 用户读写执行
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
---- ----
S_IRWXG 组读写执行
S_IWGRP 组写
S_IXGRP 组执行
S_IRGRP 组读
---- ----
S_IRWXO 其他读写执行
S_IWOTH 其他写
S_IXOTH 其他执行
S_IROTH 其他读

保存正文:

该程序第一次执行并结束时,正文部分的一个副本仍然被保留在交换区,这使得下次执行时该程序能较快的装入内存区
现在已经不需要这项技术
对一个目录设置了粘住位,你只有在拥有此文件,拥有此目录,是超级用户才能删除或者更名该目录下的文件

chown fchown lchown 函数

修改文件的用户id和组id

  #include<unistd>
  int chown(const char* pathname,uid_t owner,gid_t group);
  int fchown(int fd , uid_t owner ,gid_t group);
  /*lchown更改符号链接本身的所有者,而不是符号链接指向的文件*/
  int lchown(const char* pathname, uid_t owner , gid_t group);

返回值:

成功返回0,失败返回-1

只有超级用户进程才能修改文件的用户id

进程拥有此文件,可以修改文件的组id,但只能该到自己所属的组

文件长度

start结构体中的st_size 表示以字节为单位的文件长度且只对普通文件目录文件和符号链接有意义

普通文件:文件长度可以是0

目录:文件长度通常是一个数(16或者512的整数倍)

符号链接:文件长度是文件名的实际字节数,如usr/lib文件长度为7(并不包含c语言最后的null)

大部分unix系统提st_blksize(对文件较合适的块长度)和st_blocks(所分配实际的块的个数)

文件系统

我们可以把一个磁盘分为多个分区,每一个分区都有一个文件系统

i节点是固定长度的记录项,他包含了文件的大部分信息,并指向该文件要包含的数据块

图示为硬链接

每个目录块都包含了一个以文件名和它i节点号为表项的表

每个i节点都有一个链接计数,表示有多少指向该i节点的目录项数,当这个记数减少到0的时候文件才会彻底删除(解除对一个文件的链接)

link,unlink,remove和rename函数

任何一个文件可以有多个目录项指向其i节点,创建一个指向现有文件的链接:link

  #include<unistd.h>
  int link(const char *existingpath ,const char* newpath );

引用现有的文件existingpath创建一个新的文件newpath,如果newpath已经存在返回出错

返回值:

成功返回0,失败返回-1

删除一个现有的目录项使用ulink

  #include <unistd.h>
  int ulink(const char * pathname);

删除目录项,并将pathname引用的文件链接计数-1(如果其它地方还是有链接还是能访问此文件)

如果设置了粘住位只有在拥有该文件,具有超级管理员权限才可以删除

返回值:

成功返回0,失败返回-1

remove函数

解除该路径对一个文件或者一个目录的链接

  #include <stdio.h>
  int remove (const char *pathname);

返回值:

成功返回0,失败返回-1

rename函数

文件或者目录更名

  #include<stdio.h>
  int rename (const char* oldname,const char new name);

返回值:

成功返回0,失败返回-1

符号链接

符号链接就是一个指向文件的间接指针

  # include <unistd.h>
  int symlink(const char * path ,const char * sympath);

创建一个指向path的新目录项sympath,创建时并不要求path存在,path和sympath不一定在一个文件系统中

  /*读取符号链接*/
  #include <unist.h>
  size_t readlink(const char * restrict pathname , char restrict buf,size_t bufsize );

返回值:

成功则返回读到的字节数,失败则返回-1

文件的时间

st_atime、文件数据的最后访问时间
st_mtime、文件数据的最后修改时间
st_ctime、i节点状态的最后修改时间

目录相关函数

  /*dirent至少有如下两个成员*/
  struct dirent{
        ino_t d_ino;/*节点*/
        char d_name [NAME_MAX+1];/*文件名*/
  }
  
  #include <dirent.h>

  /* 打开一个目录 */
  DIR *opendir(const char *name);    // 成功返回指针,失败返回NULL
  DIR *fdopendir(int fd);                     // 成功返回指针,失败返回NULL

  /* 读取目录中的内容,如文件、子目录 */
  struct dirent *readdir(DIR *dirp);     // 成功返回指针,失败返回NULL

  /* 让目前的读取位置还原到开头的读取位置 */
  void rewinddir(DIR *dirp);

  /* 设置相对于开头偏移值为pos的读取位置 */
  void seekdir(DIR *dirp, long int pos);

  /* 关闭目录 */
  int closedir(DIR *dirp);                // 成功时返回0,失败返回-1

  /* 例子 */
  DIR* dir = opendir("../");
  struct dirent* ent;
  while(ent=readdir(dir)) // 1 读, 2 =,3 判断ent是否为0
  {
      printf("%d, %s\n", ent->d_type, ent->d_name);
  }    // d_tpye == 4 的是目录
  closedir(dir);

chdir、fchdir、getcwd函数

用以下两个函数可以改变当前工作目录

  #include<unist.h>
  int chdir(const char * pathname);
  int fchdir(int fd); 

getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数size为buf的空间大小。
缓冲区必须有足够大的空间足以容纳下绝对路径加上一个null ,否则返回出错
#include<unist.h>
char *getcwd(char * buf, size_t size );

posted @ 2021-01-22 20:29  陌天森  阅读(154)  评论(0)    收藏  举报