本章描述的函数被称为不带缓冲的IO,这些不带缓冲的IO函数不是ISO C标准的组成部分。但是,它们是POSIX.1和Single UNIX Specification的组成部分。
1.文件描述符
按照惯例,UNIX系统shell把文件描述符0与进程的标准输入关联;文件描述符1与标准输出关联;文件描述符2与标准错误关联。0,1,2通常替换成符号常量STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。
2.函数open
调用open函数可以打开或创建一个文件。
#include <fcntl.h>
int open(const char *path, int oflag, ...);
最后参数写为...是因为ISO C用这种方法表明余下参数数量及其类型是可变的。对于open函数而言,仅当创建新文件时才使用最后参数。
path参数是要打开或创建文件的名字;oflag参数可用来说明此函数的多个选项。用下列一个或多个常量进行“或”运算构成oflag参数。
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
O_APPEND 每次写时都追加到文件尾端
O_CREAT 若此文件不存在则创建它
O_TRUNC 将文件长度截断为0(类似将文件清空重写)
函数若成功,返回文件描述符;否则,返回-1。
3.函数close
调用close函数关闭一个打开文件。
#include <unistd.h>
int close(int fd);
参数fd为文件描述符;
函数若成功,返回0;否则,返回-1;
关闭一个文件时还会释放该进程加在该文件上的所有记录锁,之后会讨论这一点。
当一个进程终止时,内核自动关闭它所有的打开文件。很多程序都利用这一点而不显式地用close关闭文件。
4.函数lseek
每个打开文件都有一个与其相关联的“当前文件偏移量”,通常是一个非负整数,用以度量从文件开始处计算的字节数。
通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。按默认情况,当打开一个文件时,除非指定O_APPEND选项,否则偏移量被设置为0。
调用lseek函数可以为一个打开文件设置偏移量。该偏移量用于下一次读写操作。
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数offset和参数whence有关:
当whence是SEEK_SET,则将文件的偏移量设置为据文件开始处offset个字节;
当whence是SEEK_CUR,则将文件的偏移量设置为其当前值加offset,offset可正可负;
当whence是SEEK_END,则将文件的偏移量设置为文件长度加offset,offset可正可负。
若函数成功,返回新的文件偏移量;否则,返回-1。
lseek函数可以实现文件内容拓展(当offset大于0,whence为SEEK_END时),但需要在使用函数后要再进行一次写操作(无论写入多大的数)。
5.函数read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
读操作从文件的当前偏移量处开始,在成功返回之前,该偏移量将增加实际读到的字节数。
函数成功,返回读到的字节数,若已到文件尾,返回0;否则,返回-1;
注意:多种情况可能使实际字节数少于要求字节数:
(1)文件内字节数小于要求读的字节数;
(2)从终端设备读时,通常一次最多读一行(后面会讲如何改变);
(3)从网络读时,网络缓冲机制可能造成返回值小于要求读字节数;
6.函数write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
函数成功,返回已写字节数;否则,返回-1。
注意:对UNIX系统而言,文本文件与二进制文件并无区别,所以程序对两种文件都有效。
7.文件共享
每个进程在进程表中都有一个记录项,记录项中包含一张文件描述符表,其中包含:
1.1 文件描述符标志
1.2 指向一个文件表项的指针
基于1.2,文件表项包含:
2.1 文件状态标志(读、写、同步、非阻塞)
2.2 当前文件偏移量
2.3 指向该文件V节点表项的指针
基于2.3,V节点表项包含:
3.1 文件类型
3.2 对此文件进行操作函数的指针
3.3 对于大多数文件,还有I节点
基于3.3,I节点包含
4.1 文件的所有者、文件长度、指向文件实际数据块在磁盘上所在位置的指针
当两个进程打开了同一文件时,指向V节点表项的指针相同。
8.原子操作
任何要求多于一个函数调用的操作都不是原子操作,因为在两个函数调用之间,内核有可能临时挂起进程,引发错误。
9.函数dup和dup2
#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
有dup返回的新文件描述符一定是当前可用文件描述符中的最小值。对于dup2,可以用fd2参数指定新描述符的值。如果fd2已经打开,则先将其关闭。
该函数可以对已存在的文件描述符创建一个引用。
函数成功返回新的文件描述符;函数失败,返回-1。
10.函数fcntl
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
fcntl函数有以下5中功能:
(1)复制一个已有的描述符(cmd = F_DUPFD或F_DUPFD_CLOEXEC)
(2)获取/设置文件描述符标志(cmd = F_GETFD或F_SETFD)
(3)获取/设置文件状态标志(cmd = F_GETFL或F_SETFL)
(4)获取/设置异步I/O所有权(cmd = F_GETOWN或F_SETOWN)
(5)获取/设置记录锁(cmd = F_GETLK、F_SETLK或F_SETLKW)
第三个参数是一个整数。
11.函数access
#include <unistd.h>
int access(const char *pathname, int mode);
检查文件是否具有某种mode权限(读、写、执行)。
函数成功返回0;函数失败返回-1。
12.函数chmod
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
修改文件权限;
函数成功返回0;函数失败返回-1。
或者直接在命令行使用。
chmod + rwx/777 + 文件名
chmod + u/g/o/a + +/-= + rwx + 文件/目录
13.函数chgrp
chgrp -R + 用户组 + 目录名/文件名
改变目录/文件当前所在用户组。
参数-R表示进行递归修改。如果是目录,则将目录里的所有文件、目录都改变。
14.函数chown
chown + 用户名 + 文件/目录名
改变当前文件/目录的拥有者。
chown + 用户名:用户组 + 文件/目录名
改变当前文件/目录的用户组和用户。
chown + 用户名.用户组 + 文件/目录名
改变当前文件/目录的用户组和用户。
chown + .用户组 + 文件/目录名
改变当前文件/目录的用户组。
15.函数cp
cp + 源文件 + 新文件
将源文件复制一份(包括复制执行者的属性和权限)。