在Linux操作系统中,一切皆是文件—— "Everything is a file"。
如果要在Linux系统中编写操作文件的代码,需要借助文件描述符。
文件的索引——文件描述符(file descriptor):
文件描述符是一个非负整数,当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。文件描述符就是内核为了高效管理已被打开的文件所创建的索引值。 文件描述符指向内核为每一个进程维护的打开文件记录表。当要处理文件时,将文件对应的文件描述符作为参数传给函数。
文件描述符在用户进程和内核之间的映射关系:
Linux中查看文件描述符的指令:lsof(list open files)
lsof可以打开的文件:
普通文件
目录
网络文件系统的文件
字符或设备文件
(函数)共享库
管道,命名管道
符号链接
网络文件(例如:NFS file、socket)
其它类型的文件等
样例:
lsof -u username --列出某个用户打开的文件信息
lsof -c mysql --列出某个程序进程所打开的文件信息, 也可以用:lsof | grep mysql
lsof -p 11968 --通过某个进程号显示该进程打开的文件
lsof -i --列出所有的网络连接
lsof -i tcp --列出所有tcp 网络连接信息
*Linux中出现“ Too many open files”报错,如何解决:
将最大打开文件数增加到8000:
编辑/etc/security/limit.conf文件
将nofiles对应的值改为8000
如果只希望更改在当前会话中生效,用"ulimit -n 8000"
POSIX 标准提供的默认文件描述符:
操作系统提供的四种常见的I/O文件操作函数 (fd表示“文件描述符”):
fd = open(pathname, flags, mode)
numread = read(fd, buffer, count)
numwritten = write(fd, buffer, count)
status = close(fd)
实现I/O操作的主要函数:
1.open()
open() 函数用于打开或者创建文件。其在打开或者创建文件时可以指定文件的属性及用户的权限等各种参数。要使用 open() 函数,需要包含 #include <sys/stat.h> 和 #include <fcntl.h> 这两个头文件。下面是函数的说明:
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int openflag, [mode_t mode]);
args:
const char *path: 文件路径,可以是绝对,也可以是相对路径
int openflag : 文件打开的方式
- O_RDONLY 只读打开
- O_WRONLY 只写打开
- O_RDWR 可读可写打开
以上3种必选一个,以下4种可以任意选择
- O_APPEND 追加打开,所写数据附加到文件末
- O_CREAT 若此文件不存在则创建它
- O_EXCL 若文件存在则报错返回
- O_TRUNC 如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断为0字节
[mode_t mode] : 文件权限,只有在创建文件时需要使用
return:
文件描述符,非负整数是成功,-1是失败
常见的openflag:
2.write()
用 write() 向打开的文件写入数据,要使用这个函数,需要包含 #include <unistd.h>
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbyte);
args:
int fd : 文件描述符
const void *buf: 写入数据在内存空间存储的地址
size_t nbyte : 期待写入数据的最大字节数
return:
文件实际写入的字节数,非负整数是成功,-1是失败(磁盘已满或者超出该文件的长度等)
3.read()
读文件函数 read() ,需要包含 #include <unistd.h>
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbyte);
args:
int fd : 文件描述符
void *buf : 读取数据在内存空间存储的地址
size_t nbyte: 期待读取数据的最大字节数
return:
文件实际读取的字节数,非负整数是成功,-1是失败
4.lseek()
在每个打开的文件中都有一个文件的偏移量,文件的偏移量会根据文件的读写而改变位置。我们可以通过 lseek() 函数来调整文件的偏移量。默认情况下,新打开文件的文件偏移量在文件的开始
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
args:
int fd : 文件描述符
off_t offset: 文件偏移量移动的距离
int whence : 文件偏移量的基址
SEEK_SET 文件开始处
SEEK_CUR 文件当前位置
SEEK_END 文件结束处
return:
当前文件指针的位置,非负整数是成功,-1是失败
whence在文件中的位置如图所示:
当文件不再被使用时,可以调用 close(int fd) 函数来关闭被打开的文件。
代码样例:
Demo1:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
/**
* This is a simple example for using open(), write(), read(), lseek() and close().
*/
int main(int argc, char *argv[])
{
int fd;
ssize_t wr_size, rd_size;
char buffer[128];
char string_1[30], string_2[30] = "This is the second line!\n";
char *path = "./file_io.log";
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 511);
if (fd < 0) {
printf("File create fail...\n");
return -1;
} else {
printf("File create success...\n");
}
/* write the first line to file_io.log */
strcpy(string_1, "This is a demo for file_io!\n");
wr_size = write(fd, string_1, strlen(string_1));
if (wr_size < 0) {
printf("File write 1 fail...\n");
printf("wr_size = %d\n", wr_size);
return -1;
} else {
printf("File write 1 success...\n");
printf("wr_size = %d\n", wr_size);
}
/* write the second line to file_io.log
* in this case, we only write 10 bytes data from string_2 to file.
*/
wr_size = write(fd, string_2, 10);
/* add "\0"(not '\0'!!) to the end of the second line */
wr_size = write(fd, "\0", 1);
if (wr_size < 0) {
printf("File write 2 fail...\n");
printf("wr_size = %d\n", wr_size);
return -1;
} else {
printf("File write 2 success...\n");
printf("wr_size = %d\n", wr_size);
}
/* decrease current file offset by 20 bytes */
lseek(fd, -20, SEEK_CUR);
rd_size = read(fd, buffer, 100);
if (rd_size < 0) {
printf("File read_1 fail...\n");
printf("rd_size = %d\n", rd_size);
return -1;
} else {
printf("File read_1 success...\n");
printf("rd_size = %d,\nbuffer = %s\n", rd_size, buffer);
}
close(fd);
return 0;
}
Demo2:模拟文件拷贝
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[])
{
int inputFd, outputFd, openFlags;
mode_t filePerms;
ssize_t numRead;
char buf[BUF_SIZE];
if (argc != 3 || strcmp(argv[1], "--help") == 0)
usageErr("%s old-file new-file\n", argv[0]);
/* Open input and output files */
inputFd = open(argv[1], O_RDONLY);
if (inputFd == -1)
errExit("opening file %s", argv[1]);
openFlags = O_CREAT | O_WRONLY | O_TRUNC;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |S_IROTH | S_IWOTH; /* rw-rw-rw- */
outputFd = open(argv[2], openFlags, filePerms);
if (outputFd == -1)
errExit("opening file %s", argv[2]);
/* Transfer data until we encounter end of input or an error */
while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
if (write(outputFd, buf, numRead) != numRead)
fatal("write() returned error or partial write occurred");
if (numRead == -1)
errExit("read");
if (close(inputFd) == -1)
errExit("close input");
if (close(outputFd) == -1)
errExit("close output");
exit(EXIT_SUCCESS);
}