第四章 文件和目录

第四章 文件和目录


函数 stat、fstat、lstat、fstatat

lstat:若为符号链接,则返回该符号链接本身的信息。
fstatat:可传入指定目录文件描述符,然后传入相对路径; 可规定(通过flag)是否返回符号链接指向(默认)或是其本身。




文件类型:
普通文件、 目录文件
块特殊文件:提供对设备带缓冲的访问。每次访问以固定长度为单位进行
字符特殊文件:提供对设备不带缓冲的访问,每次访问长度可变。所有设备文件要么块、要么字符文件
FIFO(命名管道):用于进程间通信
套接字(socket):用于进程间的网络通信,也可用于在一台宿主机上进程间非网络通信。
符号链接:指向另一个文件


经常在 manpage 中发现用户或组的 id具有real 和 effective 的区别
real 识别我们究竟是谁。 这个字段在登录时取自口令文件中的登录项。通常,在一个登录会话期间这些值并不改变,但root进程有方法改变它们。
effective 决定了我们的文件访问权
通常情况下,real 和 effective是相同的
在struct stat的数据结构中,st_mode可以设置两个特殊标志:
1、当执行此文件时,将进程的有效用户ID设置为文件所有者的ID
2、当执行此文件时,将执行此文件的进程有效组ID设置为文件的组所有者的ID
这应该就是当初学文件权限时候比较特殊的 SGID 和 SUID了吧。




看到这里,回忆了一下 目录权限的作用,居然有些淡忘。
目录的存取权限
在ls命令后加上-d选项,可以了解目录文件的使用权限。
读权限(r)表示可以列出存储在该目录下的文件,即读目录内容列表。这一权限允许Shell使用文件扩展名列出相匹配的文件名。(ls)
写权限(w)表示允许用户从目录中删除或添加新的文件,通常只有文件主才有写权限。
执行权限(x)表示允许用户在目录中查找,并能用cd命令将工作目录改到该目录。


函数 access 和 faccessat
用于以进程的实际ID和实际组ID为基础执行其访问权限测试(注意使用的是 read id)
#include <stdio.h>
  #include <stdlib.h>
  #include <fcntl.h>
  
  int main(int ac,char *av[])
  {
          if(ac != 2)
                 exit(1);
 
        if(access(av[1],R_OK) == 0)                     //Test if I can read
                 printf("read access OK\n");
 
         if(open(av[1],O_RDONLY) >= 0)
                printf("start reading \n");
        return 0;
  }
生成运行程序 a.out
测试普通文件后发现一切如预期。
但是当我们将 a.out 的User改为root且将 a.out 设置了 SUID位的时候,执行./a.out /etc/password ,
access函数并没有正常显示(这就说明实际的用户ID是不能访问该文件),即权限测试失败
但是还是能通过open正常打开(因为open采用的是对进程的有效ID进行测试,即root)




函数 umask
在shell中直接输入 umask ,可显示当前默认值
之前在鸟个私房菜上对此有过一定认识,现在将其中一段复制过来
创建文件或目录若不采取措施则拥有默认权限,文件为666,目录为777.
umask 当目前用户在建立档案或目录(已拥有默认权限)后 , 最终权限是『该默认值减去umask指定的权限!』




函数 chmod、fchmod、fchmodat
fchmodat 用于改变行为,例如符号链接文件是其本身还是其指向



粘着位 S_ISVTX
如果对一个文件设置了粘着位,只有对该目录具有写权限的用户并且满足下面条件之一,才能删除或重命名该目录下文件:
拥有此文件
拥此有此目录
是超级用户
因此此粘着位可以防止用户删除其它用户的文件等等其他不错的用处




函数 chown,fchown,fchownat,lchown




文件长度:
struct stat中的 st_size 表示以字节为单位的文件的长度。只对普通文件、目录文件、符号链接有意义。
文件中的空洞:
书上举了个例子:
ls -l core 发现其文件大小  大于 du -s core(为其所占用的磁盘实际空间),这是因为core中有空洞
然后 touch core.copy ; cat core > core.copy 发现新文件所占用的磁盘空间则大于core所占,这就表明了使用如 cat 这样的实用程序会使得空洞被填满,皆填为0.


文件截断
在打开文件open中加入 O_TRUNC 可实现文件截断为0.
函数 :truncate、 ftruncate
将文件截断为指定的 length。 若length 大于文件本身长度,即创建了空洞; 否则大于length小于文件长度的部分不再访问。




文件系统[重点]

由于看这之前已经对此有过一定的了解,并且了解了操作系统书上对此的一些大致介绍,所以这里就直接贴图,为的是以后遗忘了方便看回来








函数 link、linkat、unlink、unlinkat、remove
依稀记得当初学到硬连接时候的问题,当初看到硬连接有两个标准: 1、不能跨filesystem  2、不能建立目录的硬连; 但当为我看到目录的链接计数大于1后,就产生了疑惑
这本书上说, 如果实现支持创建指向一个目录的硬连接,其也仅限于root可以这样做。
不允许普通用户建立的理由是这样做可能在文件系统中形成循环,大多数处理文件系统的实现程序不能处理这种情况
unlink用于删除一个现有的目录项
当链接计数达到0时,文件内容才会被删除,不过,还有一个条件会阻止删除文件内容:---只要有进程打开了该文件
该文件内容也就不会被删除。 在关闭一个文件时,内核首先会检查打开该文件的进程数,若为0,则再去检查其链接计数,如果计数也为0,那么删除该文件。
但是,但是,若在打开文件的时候,unlink该文件,那么该文件虽然内容没有被删除,但其目录项已不存在了。
这里在看到下一章关于 tmpfile 的时候产生了疑惑,文件被unlink后(此时有进程在用)内容存在,那我是否还能向其中添加内容呢?
所以我用以下程序测试了一下 : 
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>


int main()
{
        char name[] = "/tmp/woqu";
        char buf[BUFSIZ];
        int fd;
        int len;


        if((fd = open(name,O_RDWR | O_CREAT)) == -1)
        {
                perror("open error");
                exit(1);
        }


        unlink(name);


        len = strlen(name);


        if(write(fd,name,len) != len)
        {
                perror("write error");
                exit(1);
        }


        lseek(fd,0,SEEK_SET);


        if(read(fd,buf,BUFSIZ) <0)
        {
                perror("read error");
                exit(1);
        }


        printf("%s -----\n",buf);


        return 0;
}


输出结果是   $ ./a.out
/tmp/woqu -----
  $ ls -l /tmp/wuqo
$ ls: cannot access /tmp/woqu: No such file or directory
证明是可以向其中写入并读出数据的,且此时该文件不存在


注意,使用函数时需要注意是否接触到 符号链接,并注意操作对象是其本身还是其指向的文件。


函数 symlink 、 symlinkat
用于创建符号连接。 不过,新创建的符号链接不一定需要指向某个文件。 即 symlink(actualpath,sympath)中 actualpath不是一个已存在的文件也可以完成出创建。
因为 open 函数使用 符号链接会直接指向实际文件,所以就有了以下两个函数专门用于读取符号链接
函数 readlink 、readlinkat




文件的时间:
记录在struct stat数据结构中的三个时间:
st_atim文件数据的最后访问时间 readls -u
st_mtim 文件数据的最后修改时间 write默认
st_ctim i节点状态的最后更改时间 chmod、chown-c

当然,文件修改和i节点状态更改是不同的
文件修改即其内容改变,无论是添加、删除等

状态修改即是改变权限、更改链接数等




函数 futimens、utimensat、utimes
改变文件访问和修改时间




函数 mkdir、mkdirat、rmdir
删除目录的过程与删除文件的过程类似
在调用rmdir函数后,若在链接计数达到0时,有一个进程或多个进程打开此目录,则在此函数返回前删除最后一个链接 . 和 .. 项。另外,在此目录中不能再创建新文件,但是在最后一个进程关闭它之间并不释放此目录。 (为了使rmdir成功执行,目录必须是空的)




函数 对目录的读取
opendir、 fdopendir 打开目录
readdir 逐项读取目录项
rewinddir、telldir、seekdir 关于在目录中的位置函数
closedir
这里是使用了一系列相关函数实现了文件系统各种文件的计数 P106




函数 chdir、fchdir、getcwd
当前工作目录是进程的一个属性,所以其只影响调用 chdir 的进程本身,而不影响其他进程
看到这个,就想起之前尝试写一个简陋的shell的时候,希望实现shell的命令cd,但无论怎么cd都无法改变当前工作目录,最后才发现这个只能是内建命令。。
可以通过 getcwd 得到完整的路径名(当初我是自己根据文件系统一级一级的往上找从而得到完整路径名的,现在看看真是费劲了)
fchdir相对于chdir简单了不少,若是希望进程在进入一个新的工作目录后返回原来目录,只需要事先open原来目录得到文件描述符,最后使用fchdir返回就可以了。




最后还剩下 struct stat中的 st_dev 和 st_rdev
回忆:之前在操作系统书上的设备相关章节的时候注意到一个名词,逻辑设备号; 若没有逻辑设备号,且一个进程有I/O需求,针对一个设备发出请求,若该设备忙,则进程根据规则或等待或放弃使用;然而逻辑设备号却使得相同类型的设备具有相同的设备号,以及不同的次设备号,对I/O有需求的进程向该类设备发出请求,并在该类设备中找出一个可以用的设备。
结合实际: 切换至 /dev 目录下,使用 ls -l 可以发现设备文件类型名字后 有一组数组,中间用逗号隔开,前面可能就是该类设备逻辑设备名,也正是其设备驱动程序编号。
回到书本: 每个文件系统所在的存储设备都由其主、次设备号表示。 主设备号标志设备驱动程序;次设备号标志特定的子设备。 举例:一个磁盘驱动器经常包含若干个文件系统,在同一磁盘驱动器上的各个文件系统通常具有相同的主设备号,但是次设备号却不同。
总结: 可以发现, 书本上的更具体。




初学两章后,发现反倒是编程实践上的内容更具体,印象深刻, 而非一个个函数讲过去, 而是切合实际。
本书虽然详细,但若是作为入门, 恐怕学过一阵后会发现之前所学并没有什么印象。 整体虽然是很系统的讲解,但是细看却十分琐碎,可能是我看得还少吧。

学这本书我自己也显得浮躁,因大部分内容已经见过,所以沉不下心去看,也有我自身的原因。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值