1、目录结构
文件的属性信息保存在inode(结点)的数据结构里。
系统使用的是文件的结点编号,而子目录结构只不过是为方便人们的使用而给文件起的名字。
目录是一种用来保存结点号和其他文件的名字的特殊文件。目录文件里一个数据项实际上是一个指向代表某个文件的inode结点的链接指针,删除该文件名就等于删除与之对应的链接指针(文件的结点号可以通过ln -i命令查看)。利用ln命令,我们可以在不同的子目录里创建到同一个文件的链接。如果文件的链接个数(即ls -l命令的输出中跟在访问权限后面的那个数字)变为零,就表示该结点以及它所指向的数据不再有人使用,磁盘上的相应位置就会被标记为可用空间。
2、文件和设备
将硬件设备映射为文件
mount -t iso9660 /dev/hdc /mnt/cd_rom
cd /mnt/cd_rom
这个命令把(开机引导时被加载为dhc的)cd中的当前内容挂装为子目录/mnt/cd_rom下的文件结构。
/dev/console设备
这个设备代表的是系统控制台。
/dev/tty设备
进程控制终端(键盘和显示屏,或者键盘和窗口)的一个假名(逻辑设备)
/dev/console设备只有一个,但通过/dev/tty能够访问的物理设备却可以说是数不胜数。
/dev/null设备
所有写向这个设备的输出都讲被丢弃,而读这个设备会立刻返回一个文件尾标志,所以在cp命令里可以把它用做拷贝空文件的源文件。人们经常把不想看到的输出重定向到/dev/null
ioctl控制i/o设备
/dev中的设备文件用法都是一致的
open 打开一个文件或设备
read 从一个打开的文件活设备里读数据
write 写入一个文件或设备
close 关闭一个文件或设备
ioctl 把控制信息传递到设备驱动程序
每个驱动程序都有它自己的一套ioctl命令
3、库函数
用户程序-库-》系统调用-内核-设备驱动程序-》硬件设备
4、文件的底层访问
0 标准输入
1 标准输出
2 标准错误
4.1write
#include<unistd.h>
size write(int fildes,const void *buf,size_t nbytes);
把缓冲区buf里的前nbytes个字节写入与文件描述符files相关联的文件中去。它的返回值是实际写出的字节数。
4.2read
#include<unistd.h>
size_t read(int fildes,const void *buf,size_t nbytes);
从与文件描述符fildes相关联的文件读入nbytes个字节的数据并把它们放到数据区buf里
4.3open
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int open(const char *path,int oflags);
int open(const char *path,int oflags,mode_t mode);
open建立了一条到文件或设备的访问路径。如果操作成功,它将返回一个文件描述符,后续的read和write等系统调用就将使用该文件描述符对打开的那个文件进行操作,如果两个程序同时去打开同一个文件,会导致两个彼此不一样的文件描述符,同时对同一个文件进行读写需要文件加锁。
oflags:
O_RDONLY 以只读方式打开
O_WRONLY 以只写方式打开
O_RDWR 以读写方式打开
可选(按位或操作OR)组合:
O_APPEND 把写入数据追加在文件的末尾
O_TRUNC 把文件长度设置为灵,丢弃其中现有的内容
O_CREAT 按mode中给出的访问模式创建文件
O_EXCL 与O_CREAT一起使用,确保调用者创建出文件夹。open是一个最小规模的操作,也就是说,它只完成一个函数调用。而加上这个可选模式可以防止两个程序同时创建一个文件情况的出现。如果文件已经存在,open操作将失败。
4.4访问权限的初始化值
open中的mode:sys/stat.h
S_IRUSR 读权限,文件属主
S_IWUSR 写,文件属主
S_IXUSR 执行,主
S_IRGRP 读,文件所在分组
S_IWGRP
S_IXGRP
S_IROTH 读,其他用户
S_IWOTH
S_IXOTH
4.5umask
是一个系统变量,它的作用是位文件的访问权限设定一个掩码,再把这个掩码用在文件创建操作中。这是一个由3个8进行数字组成的值。各数字都是八进制1、2、4的AND操作结果。这三个数字分别对应用户(user)、分组(group)和其他用户(other)的访问权限。
数字 取值 含义
1 0 不禁止任何属主权限
4 禁止属主的读权限
2 禁止属主的写权限
1 禁止属主的执行权限
2 0 不禁止任何分组权限
4 禁止分组的读权限
2 写
1 执行
3 0 不禁止任何其他用户权限
4 禁止 读
2 写
1 执行
当我们通过一个open或creat调用创建一个文件的时候,mode参数将与umask进行比较。mode参数中被置位了的位如果在umask中也被置位了,就会被排除在访问权限的构成之外。
4.6close系统调用
#include<unistd.h>
int close(int fildes);
4.7ioctl
#include<unistd.h>
int loctl(int files,int cmd,...);
unistd.h = unix standard header 其本身并不是C语言自带的,包含unix/linux的系统调用函数的声明,如 read,getpid等等
stdlib.h = standard library header 包含标准库函数的声明
stdio.h = standard input & output header 标准输入输出函数的声明,如printf等
4.8lseek
#include<unistd.h>
#include<sys/types.h>
off_t lseek(int fildes,off_t offset,int whence);
设置文件的下一个读写位置。offset参数用来指定位置,whence定义该偏移值的用法。whence可以取下列之一
SEEK_SET offset是一个绝对位置
SEEK_CUR offset是从当前位置算起的一个相对位置
SEEK_END offset是从文件尾算起的一个相对位置
lseek的返回值是从文件头到文件指针被设置处的字节偏移值
指针移动操作中偏移值offset的类型off_t是一个与操作系统具体实现有关的类型,它的定义在文件sys/types.h里
2、fstat,stat和lstat系统调用
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
int fstat(int fildes,struct stat *buf);
int stat(const char *path,struct stat *buf);
int lstat(const char *path,struct stat *buf);
fstat返回的是与一个打开的文件描述符关联着的文件的状态信息。这些信息将被写到一个buf结构里,其地址作为一个参数被传递过去。
3、dup和dup2
#include<unistd.h>
int dup(int fildes);
int dup2(int fildes,int fildes2);
提供了复制文件描述符的一个方法,使人们能够通过两个或者更多个不同的描述符来访问同一个文件,在文件的不同位置对数据进行读写。
五、标准i/o库
在一个程序开始运行的时候,有三个文件流是自动打开好了的。它们是stdin、stdout和stderr。
分别对应着底层文件描述符0、1、2.
5.1fopen
FILE *fopen(const char *filename,const char *mode);
r/rb 以只读方式打开
w/wb 以写方式打开,并把文件长度截短为零
a/ab 以写方式打开,新内容追加在文件尾
r+/rb+/r+b 以修改方式打开(读和写)
w+/wb+/w+b 以修改方式打开,并把文件长度截短为零
a+/ab+/a+b 以修改方式打开,新内容追加在文件尾
字母b表示文件是一个二进制文件而不是一个文本文件。
unix与dos的一个不同之处就在于它并不明确区分文本文件和二进制文件,都看作二进制文件,mode参数必须是一个字符串,不是一个字符。所以是“r”不是‘r’
5.2fread
size_t fread(void *ptr,size_t size,size_t nitems,FILE *stream)
从一个文件流里读取数据。数据从文件流stream被读到由ptr指定的数据缓冲区里。
5.3fwrite
fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream)
5.4fclose
int fclose(FILE *stream)
可用文件流的个数是有上限的,就像文件描述符的个数也有上限一样,stdio.h定义FOPEN_MAX,最少要设置为8
5.5fflush
int fflush(FILE *stream)
把文件流里的现有数据立刻写入文件,调用fclose隐含着执行一次fflush
5.6fseek
int fseek(FILE *stream,long int offset,int whence);
它在文件流里为下一次读或写操作设置偏移位置。
5.7、fgetc、getc、getchar
int fgetc(FILE *stream);
从文件流里取出下一个字节并把它当做一个字符返回,当它到达文件尾或出现错误时,返回EOF,用户通过ferror或feof来区别这两种情况。
int getc(FILE *stream);
int getchar();
5.8fputc、putc、putchar
int fputc(int c,FILE *stream);
把一个字符写到一个输出文件流里去。它的返回值是它刚写的那个值,如果失败,则返回EOF
int putc(int c,FILE *stream);
int putchar(int c);
5.9fgets、gets
char *fgets(char *s,int n,FILE *stream);
char *gets(char *s);
5.10printf、fprintf和sprintf
int printf(const char *format,...);
int sprintf(char *s,const char *format,...);
int fprintf(FILE *stream,const char *format,...);
2scanf、fscanf、sscanf
int scanf(const char *format,...);
int fscanf(FILE *stream,const char *format,...);
int sscanf(const char *s,const char *format,...);
5.11、fgetpos 获得文件流当前读写位置
fsetpos 设置文件流的当前读写位置
ftell 返回文件流当前读写位置的偏移值
rewind 重置文件流里的文件读写位置
freopen 重新使用一个文件流
setvbuf 为文件流设置缓冲策略
remove 相当于unlink函数,但如果它的path参数是一个子目录的话,其作用就相当于rmdir函数
5.12文件流错误处理
#include<errno.h>
extern int errno;
#include<stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);
void clearerr(FILE *stream);
ferror对文件流的出错指示器进行测试,如果它被置位,就返回非零值,否则返回零
feof在一个文件流里对它的文件尾指示器进行测试,如果它被置位,就返回非零值,否则返回零
if(feof(some_stream))
//we're at the end
clearerr是重置由stream指定的文件流的文件尾指示器和出错指示器
5.13文件流和文件描述符的关系
每个文件流都和一个底层文件描述符相互关联着。
#include<stdio.h>
int fileno(FILE *stream);
FILE *fdopen(int fileds,const char *mode);
通过调用fileno函数确定文件流使用的是哪个底层文件描述符。它返回的是给定文件流使用的文件描述符,-1表示调用失败
通过调用fdopen在一个已经打开的文件描述符的基础上创建一个新的文件流,这个函数的基本作用是位一个已经打开的文件描述符提供一个stdio缓冲区,这样对它的读写操作可能比较容易进行一些
6、文件和子目录的维护
6.1chmod
#include<sys/stat.h>
int chmod(const char *path,mode_t mode);
6.2chown
超级用户可以使用改变一个文件的属主
#include<unistd.h>
int chown(const char *path,uid_t owner,gid_t group);
6.3unlink、link、symlink
我们可以用unlink来删除一个文件
#include<unistd.h>
int unlink(const char *path);
通过减少指定文件伤的链接计数达到删除目录数据项的目的,如果你想通过调用这个函数删除子目录中某个文件所对应的目录数据项,就必须拥有这个子目录的写和执行权限
int link(const char *path1,const char *path2);
创建某个文件的新链接
int symlink(const char *path1,const char *path2);
6.4mkdir、rmdir
#include<sys/stat.h>
int mkdir(const char *path,mode_t mode);
int rmdir(const char *path);
6.5chdir、getcwd
#include<unistd.h>
int chdir(const char *path);==cd
int *getcwd(char *buf,size_t size);=pwd
把当前子目录的名字写到给定的缓冲区buf里
3.7扫描子目录
7.1opendir
#include<sys/types.h>
#include<dirent.h>
DIR *opendir(const char *name);
打开一个子目录并建立一个子目录流,目录数据项的读操作就是通过这个指针来完成的
7.2readdir
#include<sys/types.h>
#include<dirent.h>
struct dirent *readdir(DIR *dirp);
指针指向的结构里保存着子目录流dirp中下一个目录数据项的有关资料。后续的readdir调用将返回后续的目录数据项的有关资料。
dirent结构中包含着的目录数据项内容包含以下数据:
ino_t d_ino 文件的inode
char d_name[] 文件的名字
要想进一步查明子目录中某个文件的详细资料,还需要再使用一个stat系统调用
7.3telldir
#include<sys/types.h>
#include<dirent.h>
long int telldir(DIR *dirp);
返回值记录着子目录里的当前位置。我们可以在随后的seekdir调用里利用这个值对当前位置再做一次子目录扫描
7.4seekdir
#include<sys/types.h>
#include<dirent.h>
void seekdir(DIR *dirp,long int loc);
对dirp指定的子目录流中的目录数据项的指针进行设置。loc用来设置指针位置,它应该通过前一个telldir调用获得
7.5closedir
#include<sys/types.h>
#include<dirent.h>
int closedir(DIR *dirp);
关闭一个子目录流并释放与之关联的资源。