今天玩了会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)或者直接跳过空洞。