1.1. 文件描述符:内核为每个进程维护一个已打开文件的记录表,文件描述符是一个较小的正整数(0—1023),它代表记录表的一项,通过文件描述符和一组基于文件描述符的文件操作函数,就可以实现对文件的读、写、创建、删除等操作。常用基于文件描述符的函数有open(打开)、creat(创建)、close(关闭)、read(读取)、write(写入)、ftruncate(改变文件大小)、lseek(定位)、fsync(同步)、fstat(获取文件状态)、fchmod(权限)、flock(加锁)、fcntl(控制文件属性)、dup(复制)、dup2、select和ioctl。(具体的函数如果你不知道怎么使用你可以使用man 函数名来查看帮助)
1.2. 开、创建和关闭文件
open和creat都能打开和创建函数,原型为
#include <sys/types.h> //头文件
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags); //文件名 打开方式
int open(const char *pathname, int flags, mode_t mode);//文件名 打开方式 权限
int creat(const char *pathname, mode_t mode); //文件名 权限 //现在已经不常用了
creat函数等价于èopen(pathname,O_CREAT|O_TRUNC|O_WRONLY,mode);
open()函数出错时返回-1,相关参数如下:
flags和mode都是一组掩码的合成值,flags表示打开或创建的方式,mode表示文件的访问权限。
flags的可选项有
掩码 | 含义 |
O_RDONLY | 以只读的方式打开 |
O_WRONLY | 以只写的方式打开 |
O_RDWR | 以读写的方式打开 |
O_CREAT | 如果文件不存在,则创建文件 |
O_EXCL | 仅与O_CREAT连用,如果文件已存在,则强制open失败 |
O_TRUNC | 如果文件存在,将文件的长度截至0 |
O_APPEND | 已追加的方式打开文件,每次调用write时,文件指针自动先移到文件尾,用于多进程写同一个文件的情况。 |
O_NONBLOCK | 非阻塞方式打开,无论有无数据读取或等待,都会立即返回进程之中。 |
O_NODELAY | 非阻塞方式打开 |
O_SYNC | 同步打开文件,只有在数据被真正写入物理设备设备后才返回 |
注意:LINUX中基于文件描述符的open函数,对于一个不存在的文件,不能通过O_WRONLY的方式打开,必须加上O_CREAT选项。
1.3. 读写文件
读写文件的函数原型为:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);//文件描述词 缓冲区 长度
ssize_t write(int fd, const void *buf, size_t count);
对于read和write函数,出错返回-1,读取完了之后,返回0, 其他情况返回读写的个数。
Example:将aaa.txt中的内容复制到bbb.txt中,其中bbb.txt起初不存在。
#include <stdio.h>
#include <stdlib.h> //包含exit
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h> //用perror输出错误
#include <unistd.h>
#define FILENAME1 "./aaa.txt" //用宏定义文件的路径,可以实现一改都改
#define FILENAME2 "./bbb.txt"
main()
{
char buf[512] = {0};
int fo1 = open(FILENAME1, O_RDONLY);//fo1,fo2都是文件描述词
int fo2 = creat(FILENAME2, 0755); //创建文件
//int fo2 = open(FILENAME2, O_WRONLY | O_CREAT);
if( (-1 == fo1) || (-1 == fo2) )
{
perror("open failed!\n");
//用于输出错误信息.类似于:fputs(”open failed\n”,stderr);
exit(-1);
}
int fr = 0;
while( (fr = read(fo1, buf, sizeof(buf))) > 0 )
//如果read读取成功,返回的是长度,否则,返回-1
{
int fw = write(fo2, buf, fr);
if( -1 == fw )
{
perror("write failed!");
exit(-1);
}
}
close(fo1);
close(fo2);
}
1.4. 文件定位
函数lseek将文件指针设定到相对于whence,偏移值为offset的位置
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);//fd文件描述词
whence 可以是下面三个常量的一个
SEEK_SET 从文件头开始计算
SEEK_CUR 从当前指针开始计算
SEEK_END 从文件尾开始计算
利用该函数可以实现文件空洞(对一个新建的空文件,可以定位到偏移文件开头1024个字节的地方,在写入一个字符,则相当于给该文件分配了1025个字节的空间,形成文件空洞)通常用于多进程间通信的时候的共享内存。
int main()
{
int fd = open("c.txt", O_WRONLY | O_CREAT);
lseek(fd, 1024, SEEK_SET);
write(fd, "a", 1);
close(fd);
return 0;
1.5. 获取文件信息
可以通过fstat和stat函数获取文件信息,调用完毕后,文件信息被填充到结构体struct stat变量中,函数原型为:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf); //文件名 stat结构体指针
int fstat(int fd, struct stat *buf); //文件描述词 stat结构体指针
结构体stat的定义为:
struct stat {
dev_t st_dev; /*如果是设备,返回设备表述符,否则为0*/
ino_t st_ino; /* i节点号 */
mode_t st_mode; /* 文件类型 */
nlink_t st_nlink; /* 链接数 */
uid_t st_uid; /* 属主ID */
gid_t st_gid; /* 组ID */
dev_t st_rdev; /* 设备类型*/
off_t st_size; /* 文件大小,字节表示 */
blksize_t st_blksize; /* 块大小*/
blkcnt_t st_blocks; /* 块数 */
time_t st_atime; /* 最后访问时间*/
time_t st_mtime; /* 最后修改时间*/
time_t st_ctime; /* 创建时间 */
};
对于结构体的成员st_mode,有一组宏可以进行文件类型的判断
宏 | 描述 |
S_ISLNK(mode) | 判断是否是符号链接 |
S_ISREG(mode) | 判断是否是普通文件 |
S_ISDIR(mode) | 判断是否是目录 |
S_ISCHR(mode) | 判断是否是字符型设备 |
S_ISBLK(mode) | 判断是否是块设备 |
S_ISFIFO(mode) | 判断是否是命名管道 |
S_ISSOCK(mode) | 判断是否是套接字 |
通常用于判断:if(S_ISDIR(st.st_mode)){}
1.6. 文件描述符的复制
系统调用函数dup和dup2可以实现文件描述符的复制,经常用来重定向进程的stdin(0),stdout(1),stderr(2)。
dup返回新的文件描述符(没有使用的文件描述符的最小编号)。这个新的描述符是旧文件描述符的拷贝。这意味着两个描述符共享同一个数据结构。
dup2允许调用者用一个有效描述符(oldfd)和目标描述符(newfd),函数成功返回时,目标描述符将变成旧描述符的复制品,此时两个文件描述符现在都指向同一个文件,并且是函数第一个参数(也就是oldfd)指向的文件。
原型为:
#include <unistd.h> //头文件包含
int dup(int oldfd);
int dup2(int oldfd, int newfd);
文件描述符的复制是指用另外一个文件描述符指向同一个打开的文件,它完全不同于直接给文件描述符变量赋值,例如:
描述符变量的直接赋值:
char szBuf[32];
int fd=open(“./a.txt”,O_RDONLY);
int fd2=fd; //类似于C语言的指针赋值,当释放掉一个得时候,另一个已经不能操作了
close(fd); //导致文件立即关闭
printf(“read:%d\n”,read(fd2),szBuf,sizeof(szBuf)-1); //读取失败
close(fd2); //无意义
在此情况下,两个文件描述符变量的值相同,指向同一个打开的文件,但是内核的文件打开引用计数还是为1,所以close(fd)或者close(fd2)都会导致文件立即关闭掉。
1.7. 标准输入输出文件描述符
与标准的输入输出流对应,在更底层的实现是用标准输入、标准输出、标准错误文件描述符表示的。它们分别用STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO三个宏表示,值分别是0、1、2三个整型数字。
标准输入文件描述符 è STDIN_FILENO è 0
标准输出文件描述符 è STDOUT_FILENO è 1
标准错误输出文件描述符 è STDERR_FILENO è 2