1. close() 函数
头文件和函数声明
#include <unistd.h>
int close(int fd);
函数功能
关闭一个文件描述符
返回值
成功时返回 0。失败则返回 -1,并设置 errno 为相应的错误标志。
参数
fd:文件描述符
说明
像其它所有系统调用一样,应对 close() 的调用进行错误检查,如下所示:
if (-1 == close(fd)) {
perror("close error");
exit(EXIT_FAILURE);
}
以上代码能够捕获的错误有:
- 企图关闭一个未打开文件描述符。
- 多次关闭同一个文件描述符。
- 捕获特定文件系统在关闭操作时诊断出的错误,例如:NFS(网络文件系统),如果 NFS 出现提交失败,这意味着数据没有抵达远程磁盘,随之将这一错误作为 close() 调用失败的原因传递给应用程序。
例子
以下程序功能:打开 "./log.txt" 文件,然后调用 close() 函数关闭打开的文件
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int flags = O_WRONLY | O_CREAT | O_APPEND; /* 只写,没有该文件则创建,写时从文件内容的末尾附加新内容 */mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */int fd = open("./log.txt", flags, mode); if (fd < 0) {perror("open error");exit(EXIT_FAILURE);}int ret = close(fd); /* 关闭文件描述符 */if (ret < 0) {perror("close error");exit(EXIT_FAILURE);}printf("process exit success\n");return 0;
}
2. lseek() 函数
每个打开的文件都有一个与其相关联的当前文件偏移量(current file offset),它通常是一个非负整数,用以度量从文件开始处计算的字节数。
读写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。
系统默认的情况,当打开一个文件时,除非指定 O_APPEND 选项打开,否则该偏移量被设置为 0。可以调用 lseek() 函数为一个打开的文件设置偏移量。
头文件和函数声明
#include <sys/types.h>
#include <unistd.h>
typedef long off_t;
off_t lseek(int fd, off_t offset, int whence);
函数功能
为一个打开的文件设置偏移量
返回值
成功返回新的文件偏移量,失败返回 -1,并设置 errno 为相对应的错误标志。(注意:成功返回新的文件偏移量可能为负数,所以在判断 lseek() 函数一定要谨慎,应该判断返回值是否等于 -1)
参数
fd:文件描述符
offset:参数 offset 的含义跟参数 whence 的值有关
- whence = SEEK_SET 时,则将文件的偏移量设置为文件开始处 offset 个字节,参数 offset 必须为非负数。
- whence = SEEK_CUR 时,则将文件的偏移量设置为 当前文件的偏移量 + offset 个字节,参数 offset 可以为正数,也可以为负数。
- whence = SEEK_END 时,则将文件的偏移量设置为 文件长度 + offset 个字节,参数 offset 可以为正数,也可以为负数。
off_t new_offset = lseek(fd, 0, SEEK_SET); // 将 fd 引用的文件的文件偏移量设置到文件开始处
off_t new_offset = lseek(fd, 0, SEEK_END); // 将 fd 引用的文件的文件偏移量设置到文件尾部
off_t new_offset = lseek(fd, -1, SEEK_END); // 将 fd 引用的文件的文件偏移量设置到文件尾部的前一个字节处
linux 内核中对 SEEK_SET、SEEK_CUR、SEEK_END 的定义如下所示:
#define SEEK_SET 0 /* seek relative to beginning of file */
#define SEEK_CUR 1 /* seek relative to current file position */
#define SEEK_END 2 /* seek relative to end of file */
例子
以下程序功能:打开 "./log.txt" 文件,调用 lseek() 函数将文件偏移量设置为文件尾部,然后从文件尾部开始写入 “hello world\n” 字符串,最后调用 close() 函数关闭打开的文件
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int flags = O_WRONLY | O_CREAT; /* 只写,没有该文件则创建,文件偏移量默认为 0 */mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */int fd = open("./log.txt", flags, mode); if (fd < 0) {perror("open error");exit(EXIT_FAILURE);}off_t new_offset = lseek(fd, 0, SEEK_END); // 将文件偏移量设置为文件尾部,也可用于获取文件长度(文件所占字节数)的方法if (-1 == new_offset) {perror("lseek error");exit(EXIT_FAILURE);}off_t length = new_offset;printf("before write, file length = %ld\n", length);char buf[] = "hello world\n"; // strlen(buf) = 12; sizeof(buf) = 13ssize_t nwrite = write(fd, buf, strlen(buf));if (nwrite < 0) {perror("write error");exit(EXIT_FAILURE);}length = lseek(fd, 0, SEEK_END);if (-1 == length) {perror("lseek error");exit(EXIT_FAILURE);}printf("after write, file length = %ld\n", length);if (-1 == close(fd)) {perror("close error");exit(EXIT_FAILURE);}return 0;
}
3. read() 函数
头文件和函数声明
#include <unistd.h>
/********************/
/* 32位系统 */
typedef unsigned int size_t ;
typedef int ssize_t ;
/* 64位系统 */
typedef unsigned long size_t ;
typedef long ssize_t;
/********************/
ssize_t read(int fd, void *buf, size_t count);
函数功能
从文件描述符 fd 引用的文件中请求读取 count 字节的数据并存储到从 buf 开始的缓冲区中
返回值
- 如果 read() 成功,将返回实际读取到的字节数。
- 如果遇到文件结尾(EOF)则返回 0。
- 如果读取失败,则返回 -1,并设置 errno 为相应的错误标志。
说明
一次 read() 调用所读取到的字节数可能会小于请求的字节数。
- 对于普通文件而言,这可能读取到靠近文件尾部了。
- 对于其它文件类型,比如管道、FIFO、socket 或终端,在不同环境下也会出现读取到的字节数小于请求的字节数。例如,默认情况下,从终端读取字符,遇到换行符(\n),read() 调用就会返回。
参数
fd:文件描述符
buf:存储读取到的数据的首地址
count:请求读取 count 字节数据
4. write() 函数
头文件和函数声明
#include <unistd.h>
/********************/
/* 32位系统 */
typedef unsigned int size_t ;
typedef int ssize_t ;
/* 64位系统 */
typedef unsigned long size_t ;
typedef long ssize_t;
/********************/
ssize_t write(int fd, const void *buf, size_t count);
函数功能
向文件描述符 fd 引用的文件中写入从 buf 开始的缓冲区中 count 字节的数据。
返回值
成功时返回被写入的字节数(若为 0 则表示没有写入数据)。失败则返回 -1,并设置 errno 为相应的错误标志。
参数
fd:文件描述符
buf:要被写入文件的数据在内存中的首地址
count:请求写入 count 字节
说明
如果 write() 调用成功,将会返回实际写入文件的字节数。该返回值可能会小于 count 参数值,这被叫做部分写。
- 对于磁盘文件来说,造成部分写的原因可能是磁盘已满,或是因为进程资源对文件大小的限制
- 对于磁盘文件执行 I/O 操作时,write() 调用成功并不能保证已经完成写入磁盘,因为为了减少磁盘活动量和加快 write() 系统调用,内核会缓存磁盘的 I/O 操作
例子
以下程序功能:拷贝一个普通文件。命令行格式:./copyfile_demo log.txt log2.txt
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>#define BUF_SIZE 4096int main(int argc, char *argv[])
{if (argc != 3) {printf("%s input_file out_put_file\n", argv[0]);exit(EXIT_FAILURE);}int ifd = open(argv[1], O_RDONLY); // 只读方式打开if (-1 == ifd) {fprintf(stderr, "open %s error: %s\n", argv[1], strerror(errno));exit(EXIT_FAILURE);}int flags = O_WRONLY | O_CREAT | O_EXCL; // 只读,创建新文件,O_EXCL:此标志,保证 open() 调用只能创建新文件,如果文件存在则报错int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */int ofd = open(argv[2], flags, mode);if (-1 == ofd){if (-1 == close(ifd))perror("close error");fprintf(stderr, "open %s error: %s\n", argv[2], strerror(errno));exit(EXIT_FAILURE);}char buf[BUF_SIZE] = "";ssize_t nread = 0;while ((nread = read(ifd, buf, BUF_SIZE)) > 0) {if (write(ofd, buf, nread) != nread) {if (-1 == close(ifd))perror("close error");if (-1 == close(ofd))perror("close error");perror("write error");exit(EXIT_FAILURE);}}// 成功 read() 完整个文件,nread = 0if (-1 == nread) {if (-1 == close(ifd))perror("close error");if (-1 == close(ofd))perror("close error");perror("read error");exit(EXIT_FAILURE);}if (-1 == close(ifd))perror("close error");if (-1 == close(ofd))perror("close error");printf("copy %s success\n", argv[1]);return 0;
}
注:对实例代码中 open() 函数有疑惑的小伙伴,欢迎浏览我的这篇博文,这里就不再过多阐述。
linux文件I/O之 open() 函数用法_微尘8的博客-CSDN博客
参考:
《UNIX环境高级编程》(第3版)
《Linux-UNIX系统编程手册》