1.2 基于linux的文件操作
1.2 基于linux的文件操作
- linux 下一切皆文件,socket 也是文件的一种
- 通过socket 发送数据 《=》向socket 写 数据 《=》写文件
- 读取接收到的网络数据 《=》读文件
- 关闭网络连接 《=》关闭文件
读写socket 文件的IO接口有多种:
| IO类型 | 头文件 | 接口 | 特点 |
|---|---|---|---|
| 标准 | <stdio.h> | fread,fwrite | 标准规定,平台统一。带有一个用户态的缓冲 |
| socket io | <sys/types.h> <sys/socket.h> | recv,send | socekt 专用 |
| 系统io | <sys/types.h> <sys/stat.h> <fcntl.h> <unistd.h> | read,write |
文件描述符(句柄)
打开 或 创建文件时会返回一个文件描述符,该描述符就代表了文件,通过描述符操作文件。
3个标准文件的文件描述符是固定分配的
| file descriptor | object |
|---|---|
| 0 | input |
| 1 | output |
| 2 | error |
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);brief: 按指定方式打开文件
param[in] pathname:文件名称
param[in] flags:打开方式
return -1: 出错,同时设置errno;>=0 : new file descriptor,从最小的未使用的描述符开始分配
| flag | 含义 | 说明 |
|---|---|---|
| O_RDWR | 读写 | |
| O_RDONLY | 只读 | |
| O_WRONLY | 只写 | 以上flag 必须选择其中一种 |
| O_CREAT | 文件不存在则创建 | 以下为附加选项 |
| O_TRUNC | 删除所有数据 | |
| O_APPEND | 在文件末尾添加数据 |
write
[!quote]
write() writes up to count bytes from the buffer pointed buf to the file referred to by the file descriptor fd.
最多写入count 个字节,可能出现实际写入大小小于count的情况:
- 空间不足
- 参数限制
- 信号中断
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);return -1: 出错; >= 0 实际写入的字节大小
read
[!quote]
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
在当前位置的基础上从文件中读取最多指定长度字节的数据
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);return -1:出错; >=0 : 实际读取到的字节大小
错误处理
unix 风格的函数通常在成功时返回非负值,失败时返回-1 或 null,并设置全局变量errno。通过strerror(errno) 获取错误信息
[!quote]
The strerror() function returns a pointer to a string that describes the error code passed in the argument errnum, possibly using the LC_MESSAGES part of the current locale to select the appropriate language.
#include <string.h>
char *strerror(int errnum);使用unix 风格函数时的错误处理:
#include <string.h>
void unix_error(char *msg) /* Unix-style error */
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(0);
}因为使用全局变量来存储错误码,所以每次调用unix 风格函数后都应该检查是否出错。多线程中使用strerror_r() 保证线程安全。
查看已打开的文件
使用lsof - list open files命令
- 创建一个进程并以只读的方式打开同路径下的1.txt文件
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
using namespace std;
int main()
{
int fid;
fid = open("./1.txt", O_RDONLY);
cout << "fid:" << fid << " pid:" << getpid() << endl;
while(1)
sleep(1);
return 0;
}其中1.txt 的内容为 1.txt
2. 编译并运行程序
输出:
fid:3 pid:2836- 打开另一终端,执行 lsof -p 2836, 部分结果如下
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
a.out 2836 ming 0u CHR 136,8 0t0 11 /dev/pts/8
a.out 2836 ming 1u CHR 136,8 0t0 11 /dev/pts/8
a.out 2836 ming 2u CHR 136,8 0t0 11 /dev/pts/8
a.out 2836 ming 3r REG 0,44 6 24 /media/sf_share/test/1.txt| 名称 | 值 | 含义 | 说明 |
|---|---|---|---|
| COMMAND | a.out | 进程名称 | |
| PID | 2836 | 进程号(ID) | |
| FD | 0u、1u、2u、3r | file descriptor文件描述符 | u:读写;r:只读 |
| TYPE | CHR、REG | 文件类型 | CHR-字符设备,REG-常规文件 |
| SIZE/OFF | 0t0、6 | 文件大小(常规文件)/偏移量(其他文件) | 1.txt 中字符串长度为6(5个字符+1结尾);0t 表示十进制。 |
| NODE | 11、24 | inode编号,文件在系统中的唯一标识 | |
| NAME | /dev/pts/8 /media/sf_share/test/1.txt | 路径 | pts:虚拟终端设备;8:终端号 |
可以看到进程默认就会打开3个标准设备,它们的FD 分别是0,1,2
文件类型列表:
| 类型 | 含义 |
|---|---|
| REG | 常规文件 |
| DIR | |
| CHR | 字符设备 |
| socket |