第四章 文件与I/O(1)
文件与I/O
什么是I/O
-
输入/输出是主存和外部设备之间拷贝数据的过程
- 设备->内存 (输入操作)
- 内存->设备 (输出操作)
-
高级I/O
- ANSI C提供的标准I/O库称为高级I/O,通常也称为带缓冲的I/O
-
低级I/O
- 通常也称为不带缓冲的I/O
文件描述符
ㅤ ㅤ对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的。
ㅤ ㅤ当打开或者创建一个文件的时候,内核向进程返回一个文件描述符(非负整数)。后续对文件的操作只需通过该文件描述符,内核记录有关这个打开文件的信息。
ㅤ ㅤ一个进程启动时,默认打开了3个文件,标准输入、标准输出、标准错误,对应文件描述符是0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO),这些常量定义在<unistd.h>头文件中。
文件描述符 | 文件指针 |
---|---|
STDIN_FILENO | stdin |
STDOUT_FILENO | stdout |
STDERR_FILENO | stderr |
文件描述符与文件指针转换
- fileno:将文件指针转换为文件描述符
- fdopen:将文件描述符转换为文件指针
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
//文件指针转文件描述符
printf("fileno(stdin) = %d\n",fileno(stdin));
printf("fileno(stdout) = %d\n",fileno(stdout));
printf("fileno(stderr) = %d\n",fileno(stderr));
//文件描述符转文件指针
FILE * pf = NULL;
pf = fdopen(STDOUT_FILENO, "w+");
fprintf(pf,"%s\n","hello!");
pf = fdopen(STDERR_FILENO, "w+");
fprintf(pf,"%s\n","ERROR!");
fclose(pf);
return 0;
}
文件相关系统调用
open系统调用
ㅤ ㅤ有几种方法可以获得允许访问文件的文件描述符。最常用的是使用open()(打开)系统调用
函数原型
int open(const char *path, int flags);
参数
path :文件的名称,可以包含(绝对和相对)路径
flags:文件打开模式
返回值
打开成功,返回文件描述符;
打开失败,返回-1
- 两个参数
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
//#define ERR_EXIT(m) (perror(m)),exit(EXIT_FAILURE)
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
int main(void)
{
int fd;
fd = open("test.txt", O_RDONLY);
if(fd == -1)
{
fprintf(stderr,"open error with errno = %d %s\n",errno,strerror(errno));
ERR_EXIT("open");
}
if (fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
printf("open success\n");
return 0;
}
- 三个参数
函数原型
int open(const char *path, int flags,mode_t mode);
参数
path :文件的名称,可以包含(绝对和相对)路径
flags:文件打开模式
mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限
返回值
打开成功,返回文件描述符;
打开失败,返回-1
- 文件权限
newnode = mode & ~umask
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
int main(void)
{
//如果文件存在,umask()无效
umask(0);
int fd;
fd = open("test.txt", O_WRONLY | O_CREAT, 0666);
if(fd == -1)
ERR_EXIT("open");
printf("open success\n");
return 0;
}
- 打开文件的方式
打开方式 | 描述 |
---|---|
O_RDONLY | 打开一个供读取的文件 |
O_WRONLY | 打开一个供写入的文件 |
O_RDWR | 打开一个可供读写的文件 |
O_APPEND | 写入的所有数据将被追加到文件的末尾,多进程中每个进程都会更新文件位置指针 |
O_CLOEXEC | 在执行新的进程时,文件会自动关闭 |
O_CREAT | 打开文件,如果文件不存在则建立文件 |
O_EXCL | 如果已经置O_CREAT且文件存在,则强制open()失败 |
O_TRUNC | 在open()时,将文件的内容清空 |
所有这些标志值的符号名称可以通过#include<fcntl.h>访问
- 访问权限
打开方式 | 描述 |
---|---|
S_IRUSR | 文件所有者的读权限位 |
S_IWUSR | 文件所有者的写权限位 |
S_IXUSR | 文件所有者的执行权限位 |
S_IRWXU | S_IRUSR |
S_IRGRP | 文件用户组的读权限位 |
S_IWGRP | 文件用户组的写权限位 |
S_IXGRP | 文件用户组的执行权限位 |
S_IRWXG | S_IRGRP |
S_IROTH | 文件其他用户的读权限位 |
S_IWOTH | 文件其他用户的写权限位 |
S_IXOTH | 文件其他用户的执行权限位 |
S_IRWXO | S_IROTH |
open调用的几点说明
- 可以利用按位逻辑加(bitwise-OR)(|)对打开方式的标志值进行组合。
- 如打开一个新文件:
#define NEWFILE (O_WRONLY|O_CREAT|O_TRUNC)
- 对访问权限位进行访问所用到的标识符,均可以通过
- #include <sys/stat.h> 访问到,同样可以通过|运算来对访问权限进行组合
#define MODE755 (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
close系统调用
ㅤ ㅤ为了重新利用文件描述符,用close()系统调用释放打开的文件描述符
函数原型:int close(int fd);
函数参数:
fd :要关闭的文件的文件描述符
返回值
如果出现错误,返回-1
调用成功返回0
一个进程能打开的最大文件描述符的个数
ulimit -n
一个系统能够支持打开文件的最大个数
//这个值和内存有关系,内存越大,值越大
cat /proc/sys/fs/file-max
creat系统调用
ㅤ ㅤ为了维持与早期的 UNIX 系统的向后兼容性,Linux 也提供可选的创建文件的系统调用,它称为 creat()。
函数原型:
int creat(const char *path, mode_t mode);
参数
path :文件的名称,可以包含(绝对和相对)路径
mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限
返回值
打开成功,返回文件描述符;
打开失败,返回-1
ㅤ ㅤ在UNIX的早期版本中,open() 系统调用仅仅存在两个参数的形式。如文件不存在,它就不能打开这些文件。文件的创建则由单独的系统调用 creat() 完成。在 Linux 及所有 UNIX 的近代版本中,creat() 系统调用是多余的。
fd = creat(file, mode);
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
creat() 完全等价于近代的 open() 调用