Apue.2e chapter4

今天玩了会Lubuntu,也试了一试最新的codelite,凭良心说codelite做的比code::blocks更好用,虽然功能没有后者强大,但是手感上更类似visual studio,快捷键也相仿。

另外code::blocks使用xterm太瞎眼,改为xfce4-terminal,方法是将Environments settings-General settings-Terminal to launch console programs中的字符串改为xfce4-terminal –T $TITLE –x

即可,选项可以自己man后根据情况修改。

本章继续深入文件方面的学习。

stat,fstat和lstat

#include <sys/stat.h>

int stat(const char *restrict pathname, struct stat *restrict buf); //指定路径文件的信息,符号链接返回链接文件信息

int stat(int filedes, struct stat *buf);    //指定文件描述符的相关信息

int lstat(const char *restrict pathname, struct stat *restrict buf); //如果是符号链接,返回符号链接的相关信息

struct stat{

    mode_t st_mode; //file type & mode (permissions)

    ino_t st_ino; //i-node number (serial number)

    dev_t st_dev; //device number (file system)

    dev_t st_rdev; //device number for special files

    nlink_t st_nlink; //number of links

    uid_t st_uid; //user ID of owner

    gid_t st_gid; //group ID of owner

    off_t st_size; //size in bytes, for regular files

    time_t st_atime; //time of last access

    time_t st_mtime; //time of last modification

    time_t st_ctime; //time of last file status change

    blksize_t st_blksize; //best I/O block size

    blkcnt_t st_blocks; //number of disk blocks allocated

}

红色字段为SUS扩展要求,其余为Posix.1要求。该结构被ls –l 调用。

文件类型

  • 普通文件,包括一切文本文件和可执行文件;
  • 目录文件:注意目录只能由内核进行"写"操作;
  • 块(block)设备文件:提供对设备带缓冲的访问,每次访问以固定长度为单位进行;
  • 字符(character)设备文件:提供对设备不带缓冲的访问,每次访问的长度可变;
  • FIFO:命名管道,主要用于进程间通信;
  • socket:套接字,用于进程间网络通信;
  • 符号链接:指向另一个文件。

文件类型信息存在于st_mode之中,可以通过相关宏来确认,包括S_ISREG(),S_ISDIR(),S_ISCHR(),S_ISBLK(),S_ISFIFO(),S_ISLNK(),S_ISSOCK(),参数都是mode_t格式;

POSIX.1允许将IPC对象表示为文件,可以用以下宏测试:S_TYPEISMQ()(消息队列),S_TYPEISSEM()(信号量),S_TYPEISSHM()(共享存储对象)。注意这一点在很多实现中被忽略掉了。

在Linux上,必须#define _GNU_SOURCE才能使用S_ISSOCKET这个宏!

早期UNIX不提供这些宏,需要自行编写。

UID和GID

这段主要强调了一种特殊情况:需要将文件的有效用户ID设为文件所有者ID时。

SUID特殊权限:仅对可执行文件有效,属于x的加强版,应付上述特殊情况。

SGID特殊权限:可以对目录有效,其他类似SUID。

SVTX特殊权限:和上面两个没啥关系,也是特殊权限的一种。仅对目录有效,当用户在有t权限的目录下建立档案或目录时,仅有自己和root有删除权限。

分清楚有效ID和所有者ID,前者是进程的性质,后者是文件本身的性质。

文件的访问权限

对于目录,读权限允许我们获得文件列表,执行权限允许我们通过该目录。删除一个文件需要对该文件所在的目录有写权限和执行权限

内核对于进程的权限判断分为四部,概述为 进程的有效ID是否su-是否是所有者-是否是所有组或附加组成员-其他用户,在各步骤中再确定具体的权限。

对于创建的新文件,其文件的所有者ID被设置为当前进程有效用户ID,其所属组ID被设置为当前进程的有效组ID其所在目录的组ID。

access function

#include <unistd.h>

int access(const char *pathname, int mode);

本函数以实际用户ID和实际组ID进行访问权限测试;mode分为R_OK, W_OK, X_OK, F_OK(文件是否存在)。

umask function

#include <sys/stat.h>

mode_t umask(mode_t cmask);

注意,无出错返回!cmask由9种访问权限位运算而成,分别是:

S_IRUSR, S_IWUSR, S_IXUSR

S_IRGRP, S_IWGRP, S_IXGRP

S_IROTH, S_IWOTH, S_IXOTH

注意创建的是屏蔽位(就是不能有的位)。umask修改的是创建文件的默认权限,这个改动只在该进程内有效,而不影响shell中的umask值

在shell中可以获得符号形式的表达,语法是 umask –S。

chmod, fchmod function

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);

int fchmod(int filedes, mode_t mode);

更改权限的函数,和shell中用法一致。对应符号常量包括:

S_ISUID, S_ISGID, S_ISVTX(保存正文)

S_IRWXU, S_IRUSR, S_IWUSR, S_IXUSR

S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP

S_IRWXO, S_IROTH, S_IWOTH, S_IXOTH

其中S_ISVTX是SUS扩展选项。

chmod修改的是i节点最近一次被修改的时间。

chown,fchown,lchown function

#include <unistd.h>

int chown(const char *pathname, uid_t owner, gid_t group);

int fchwon(int filedes, uid_t owner, gid_t group);

int lchown(const char *pathname, uid_t owner, gid_t group) //SUS;

如果是符号链接,最后一个更改链接的拥有者,其他两个更改链接文件的拥有者。

如果owner或group任一个为-1,那么对应的ID不变。

_POSIX_CHOWN_RESTRICTED宏规定是否允许任意用户更改其所有文件的所有者。如果=1,那么非root是不能更改文件所属用户和用户组的(除非改到自己这一组)。

文件长度

st_size字段只对reg, dir 和link有效。link的长度指的是文件名中的实际字节数。

文件截短

#include <unistd.h>

int truncate(const char *pathname, off_t length);

int ftruncate(int filedes, off_t length);

截短一个文件,使其长度为length字节,大于的部分会被截去;如果文件本身小于length,反过来会扩展该文件(取决于实现,SUS扩展行为)

文件系统

这块的内容比较难理解,注意硬链接和符号链接的区别,注意文件夹链接和文件链接的区别。

link, unlink, remove和rename

#include <unistd.h>

int link(const char *existingpath, const char *newpath);

int unlink(const char *pathname)

使用unlink可以在打开状态下取消链接,即使程序崩溃,临时文件也会被删除。

root可以unlink一个目录,但是不推荐这么干,用rmdir更好

#include <stdio.h>

int remove(const char *pathname);

int rename(const char *oldname,const char *newname)

在unix中remove和unlink效果是一样的,但是在非Unix环境下会直接删除文件。

rename可能会遇到newname已经存在的情况,其行为比较复杂。

符号链接

类似于win下的快捷方式,有些函数可以跟踪符号链接,有些则处理符号链接自身,注意分辨。

符号链接可能引起循环引用问题,简单来说,在一个文件夹中建立一个指向这个文件夹自身的快捷方式,跟踪降序遍历这个文件夹就会得到循环引用的结果。

循环引用可以通过unlink函数消除,但是硬链接就不行了,所以非super user是不能建立文件夹硬链接的。

题外话:win下也可以搞类似的玩意,但是没有GUI接口。

symlink, readlink function

#include <unistd.h>

int symlink(const char *actualpath, const char *sympathy)

sszie_t readlink(const char * restrict pathname, char *restrict buf, size_t bufsize)

注意symlink不需要actualpath实际存在

readlink是读取链接本身的字符串的,注意buf中的字符串不以null结尾。

文件的时间

st_atime    last access time        ls –u        e.g.: read

st_mtime    last modify time    default        e.g.: write

st_ctime    I node last change time    ls –c        e.g.: chmod, chown

注意系统并不保存对i节点的最后一次访问时间,所以access函数和stat函数对文件本身并无影响。

很多函数不仅影响文件本身的时间,还会影响其父文件夹的相关量。

utime function

#include <utime.h>

int utime(const char *pathname, const struct utimbuf *time);

struct utimbuf{

    time_t actime;    //atime

    time_t modtime;//mtime

};

修改时间,这里是日历时间(从1970年1月1日到现在的秒数),如果time是空指针,修改atime和mtime到当前时间。用户需要写权限或者set_uid;如果time非空,需要su或者set_uid。

ctime无法手动更新,调用utime时会自动更新(换言之,没啥能阻挡i节点更新的)。

mkdir, rmdir function

#include <sys/stat.h>

int mkdir(const char *pathname, mode_t mode);

#include <unistd.h>

int rmdir(const char *pathname);

很蛋碎的是不在同一个头文件中=。=

rmdir处理的文件夹必须是空的,否则失败。

读目录

由于Unix各种实现的差别,以及历史相关的原因,现在目录读取函数有一大堆,posix.1规定了如下函数用于目录读取:

#include <dirent.h>

DIR *opendir(const char *pathname);

struct dirent *readdir(DIR *dp);

void rewinddir(DIR *dp);

int closedir(DIR *dp);

long telldir(DIR *dp);    //SUS

void seekdir(DIR *dp, long dc);    //SUS

struct dirent{

    ino_t d_ino; //i-node number, SUS

    char d_name[NAME_MAX+1];    //null-terminated filename

};

注意NUME_MAX需要运行时确定(fpathconf)。

题外话:这里编译4-21的时候发现找不到path_alloc,实际在./apue.2e/lib中,也可以用grep –R –include='*.c' path_alloc . 找到该文件。

chdir, fchdir, getcwd

#include <unistd.h>

int chdir(const char *pathname);

int fchdir(int filedes);    //SUS

更改当前工作目录。

在shell中用pwd查看当前目录。

内核为每个进程保持当前目录信息,但是并不保存完整路径名,可以使用

char *getcwd(char *buf,size_t size);

来获得当前绝对路径。如果保存路径是为了返回,还有一种更简单的方法,使用open打开当前目录,然后使用fchdir再返回当前目录。

设备特殊文件

在struct stat中,有st_dev和st_rdev这两个dev_t值,前者标识文件系统的设备号,后者包含实际设备的设备号(只有chr和blk文件才有)。

文件系统的设备号包含主设备号和次设备号,前者标识设备驱动,后者才标定特定的主设备。可以使用宏major和minor来访问主次设备号。

 

习题:

注意4.4 creat会清空原来文件的内容,但是不改变已有文件的访问权限。

4.6 复制前先测试是否是空洞(缓冲区+逐字符),这里要求不是很清晰,可以复制空洞(用lseek)或者直接跳过空洞。

原文地址:https://www.cnblogs.com/livewithnorest/p/2863349.html