函数
lstat:不跟踪符号链接。
fstatat:路径可以是相对当前打开的目录,目录的文件描述符为fd,如果填写绝对路径,则fd被忽略。flag 设置为AT_SYMLINK_NOFOLLOW时不跟踪符号链接,默认跟踪符号链接。
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstatat(int fd,const char *path, struct stat *buf,int flag);
struct stat结构体
st_mode成员可以用相关宏来测试:例如S_ISREG()测试是否是普通文件。
POSIX.1允许将IPC(进程间通信)对象说明为文件,信号量、消息队列、共享存储对象等。用宏来测试,例如S_TYPEISMQ()测试消息队列,传入的参数是指向stat结构体的指针。
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
实验:判断输入的文件类型
1 #include <stdio.h>
2 #include"apue.h"
3
4
5 int main(int argc,char ** argv)
6 {
7 struct stat buf;
8 if(argc!=2)
9 {
10 printf("error\n");
11 }
12 lstat(argv[1],&buf);
13 if(S_ISREG(buf.st_mode))
14 printf("regular");
15 else if(S_ISDIR(buf.st_mode))
16 printf("directory");
17 else if (S_ISFIFO(buf.st_mode))
18 printf("fifo");
19 exit(0);
20 }
关于ID
与进程相关的ID:
实际用户ID 实际组ID:标识我们究竟是谁
有效用户ID 有效组ID:决定我们的访问权限
保存的设置用户ID/组ID:在执行程序时包含了有效用户ID/组ID的副本。
可以在st_mode中设置标志,是的执行时此文件时,进程的有效用户ID/组ID是文件所有者的ID。例如passwd指令。
设置用户ID/组ID:在st_mode中,用S_ISUID S_ISGID测试。
实验:目录的权限
建立一个目录hello,里面有个文件teat
将hello的权限改为000
cd hello/ bash: cd: hello/: 权限不够
chmod a+x hello d--x--x--x 可以进入目录
ls ls: 无法打开目录'.': 权限不够(读权限允许获取文件列表,这里没有读权限)
chmod a+r hello 可以执行ls命令获取文件列表了
新建文件、删除文件需要对目录有写权限
关于删除:例如删除文件test,不需要对test有权限,只需要对test所在的目录有写权限。
实验:对文件的权限
新建文件test,写入0123456789,把文件权限改为000
vim test看到内容为空(没有读权限)
往文件写入123,给文件读权限,查看文件,发现写入成功
疑问:没给写权限,为什么写入成功了?
超级用户删除文件,不需要任何权限。即使改为000,也可以删除,读、写。也就是不受权限的限制。
测试文件权限
#include<unistd.h>
access和faccessat函数:按照实际的用户ID/组ID测试。
faccessat可以设置标志位(AT_EACCESS),测试有效用户ID/组ID。
进程有效用户ID为root,有所有权限。
进程用户ID,组ID等于文件相应的ID,则根据文件相应的权限
实验:测试输入文件的访问权限。
1 #include <stdio.h>
2 #include"apue.h"
3
4 int main(int argc,char**argv)
5 {
6 if(argc!=2)
7 printf("error\n");
8 if(access(argv[1],R_OK)<0)
9 printf("access error");
10 else
11 printf("read access OK\n");
12 if(open(argv[1],O_RDONLY)<0)
13 perror("open");
14 else
15 printf("open for reading OK\n");
16 exit(0);
17 }
函数umask
#include<sys/stat.h>
mode_t umask(mode_t cmask);返回之前的文件模式创建屏蔽字
cmask:由S_IRUSR S_IWUSR等按位或组成。
执行umask命令:0002,对应为为1,则关闭那一位的相关权限。
函数
chmod
fchmod
fchmodat
改变文件的权限。
粘着位
程序第一次执行,终止后,程序正文仍然在交换分区。
改变所有者的函数
chown fchown fchownat lchown
和其他函数一样,有规律:at:表示可以相对路径定位文件,还带有flag标志位
f:表示文件描述度符
l:表示不跟踪符号链接
文件长度
stat结构体的st_size成员表示文件长度(单位:字节)
符号链接的文件长度是路径名的长度
ls -l报告的是文件长度,包括空洞的长度
du是文件的块数(Linux系统 设置了环境变量POSIXLY_CORECT,就是1024字节的块数,否则就是512),不包括空洞的长度
,空洞不占磁盘空间,但是复制文件会导致空洞被填满,填写为0.
文件截断
使用函数truncate ftruncate可以把文件截断为length(length大于原文件,可能会增加空洞)
打开文件时O_TRUNC可以把文件截断为0.
文件系统
extN能够通过inode一次性获得所有文件数据存放的位置(可以据此安排阅读顺序),fat32不可以。因此fat32会受到磁盘碎片的影响比较严重。extN也有磁盘碎片,影响不大。
df命令查看磁盘整体用量,读取superblock
sda是第一块硬盘 sdb是第二块硬盘
sda1是第一块串口硬盘的第一个分区
设备都在dev目录下,包括虚拟的和物理的
/dev/mapper/*是虚拟设备
tmpfs不对应任何设备,是真正的文件系统名称,这个文件系统是在内存中虚拟的。
目录包含目录项(文件名和inode编号)
创建链接的函数
创建新目录项和增加链接计数是原子操作。
不允许对于目录的硬链接。
关闭文件,首先检查打开文件的进程数,如果为0,检查链接计数,如果也是0,就删除文件内容。
创建临时文件的方法:open或者creat创建一个文件,然后立刻调用unlink,此时文件仍旧是打开的,不会被删除,进程结束或进程关闭文件时,文件被删除。
对于文件remove函数与unlink相同,对于目录remove与rmdir相同。
remove在stdio.h里面
文件或目录的重命名
#include<stdio.h>
rename
renameat
符号链接
硬链接直接指向inode,
目录的符号链接会导致循环(P98),由于unlink不跟随符号链接,所以可以处理。但是如果创建了构成这种循环的的硬链接,很难消除。
创建和读取符号链接
symlink
symlinkat
readlink
readlinkat
open跟随符号链接,因此可以readlink这类函数读取符号链接中的名字。
文件的时间
st_atim文件最后访问时间
st_ctim i节点状态最后修改时间
st_mtim文件最后修改时间
struct timeval {
time_t tv_sec; // seconds
long tv_usec; // microseconds
};
struct timespec {
time_t tv_sec; // seconds
long tv_nsec; // and nanoseconds
};
实验:
touch命令可以更新文件的三个时间。
1、touch hello
2、stat hello
最近访问:2018-08-30 15:36:11.451126384 +0800
最近更改:2018-08-30 15:36:11.451126384 +0800
最近改动:2018-08-30 15:36:11.451126384 +0800
3、cat hello后再查看
最近访问:2018-08-30 15:37:17.408850806 +0800//这一个变了
最近更改:2018-08-30 15:36:11.451126384 +0800
最近改动:2018-08-30 15:36:11.451126384 +0800
4、vim hello 写入123
最近访问:2018-08-30 15:38:47.015193507 +0800
最近更改:2018-08-30 15:38:47.015193507 +0800
最近改动:2018-08-30 15:38:47.071194971 +0800
5、chmod 777 hello
最近访问:2018-08-30 15:38:47.015193507 +0800
最近更改:2018-08-30 15:38:47.015193507 +0800
最近改动:2018-08-30 15:40:05.269239409 +0800//这个变了,i节点有文件权限,改编权限,改变了i 节点
目录
#include<dirent.h>
创建目录 删除目录 读目录
rmdir函数可以删除空目录
打开目录返回DIR类型指针(DIR是一个内部结构类似于FILE)
struct dirent* readdir(DIR*dp)读取一次,第二次读取下一个目录项。
struct dirent
{
long d_ino;//i节点编号
off_t d_off;//在目录中的偏移
unsigned short d_reclen;//文件名的长度
unsigned char d_type;//文件类型
char d_name [NAME_MAX+1];//文件名
}
enum
{
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
实验:读取目录
1 #include <stdio.h>
2 #include "apue.h"
3 #include <dirent.h>
4
5 int main(int argc,char** argv)
6 {
7 DIR* dir;
8 struct dirent *sdir;
9 if(argc!=2)
10 {
11 printf("error\n");
12 }
13 dir = opendir(argv[1]);
14 if(dir==NULL)
15 printf("opendir error\n");
16 while((sdir=readdir(dir))!=NULL)
17 {
18 printf("%s\n",sdir->d_name);
19 }
20 printf("read end......\n");
21 }
实验:文件遍历
注意:路径中/可以重复,例如cd /home这样是可以的
1 #include <stdio.h>
2 #include "apue.h"
3 #include <dirent.h>
4
5
6 void scan_dir(char *path,int depth)
7 {
8 DIR* dir;
9 struct dirent *sdir;
10 if(path==NULL)
11 {
12 printf("path is empty\n");
13 exit(-1);
14 }
15 dir = opendir(path);
16 if(dir==NULL)
17 {
18 printf("opendir error\n");
19 exit(-2);
20 }
21 while((sdir=readdir(dir))!=NULL)
22 {
23 if(sdir->d_type==DT_DIR)
24 {
25 if(strcmp(sdir->d_name,".")==0 || strcmp(sdir->d_name,"..")==0)
26 {
27 int tmp_depth=depth;
28 while(tmp_depth)
29 {
30 tmp_depth--;
31 printf(" ");
32 }
33
34 printf("%s\n",sdir->d_name);
35 }
36 else
37 {
38 int tmp_depth=depth;
39 while(tmp_depth)
40 {
41 tmp_depth--;
42 printf(" ");
43 }
44 printf("%s\n",sdir->d_name);
45 char str[255]={0};
46 sprintf(str,"%s/%s",path,sdir->d_name);
47 scan_dir(str,depth+1);
48 }
49 }
50 else
51 {
52 int tmp_depth=depth;
53 while(tmp_depth)
54 {
55 tmp_depth--;
56 printf(" ");
57 }
58 printf("%s\n",sdir->d_name);
59 }
60 }
61
62 }
63
64
65 int main(int argc,char** argv)
66 {
67 if(argc!=2)
68 {
69 printf("error\n");
70 }
71 scan_dir(argv[1],0);
72 printf("read end......\n");
73 }
运行结果
system.c
a.out
apue.h
..
dir.c
.
volatile.c
test
hello
..
.
hello1
hello
..
.
getpgrp.c
read end......
改变当前工作目录的函数
chdir和fchdir
获取绝对路径
getcwd
1 #include <stdio.h>
2 #include "apue.h"
3
4
5 int main()
6 {
7 char buf[255]={0};
8 getcwd(buf,255);
9 printf("%s\n",buf);
10 }