文章目录
1.系统接口
1.1 打开文件open()
int open(const char *pathname, int flags)
;int open(const char *pathname, int flags, mode_t mode)
;
参数:pathname带路径文件名;flags文件的打开方式;mode文件的权限。
返回值:打开文件成功,返回新打开的文件的文件描述符
,失败则返回-1。
flags文件的打开方式:
O_RDONLY
: 只读打开O_WRONLY
: 只写打开O_RDWR
: 读,写打开
- 这三个常量,必须指定一个且只能指定一个。
O_CREAT
: 若文件不存在,则创建它。O_TRUNC
: 覆盖写(每次写入删除原内容,将本次内容写入)。O_APPEND
: 追加写(每次写入不删除原内容,将本次内容写入)。
- 用上面的1,2,3和下面的1,2,3 的一个或者多个常量进行“或”运算。
- 例:O_WRONLY | O_CREAT。
1.2 关闭文件close()
int close(int fd)
;
参数:要关闭的文件的文件描述符。
返回值:成功返回0,失败返回-1。
1.3 读/写文件 write()/read()
1.3.1读文件write()
ssize_t write(int fd, const void *buf, size_t count)
;
参数:fd文件描述符;buf写入的字符串;count写入的大小。
返回值:成功返回写入成功的字节数,失败返回-1。
1.3.2 写文件read()
ssize_t read(int fd, void *buf, size_t count)
;
参数:fd文件描述符;buf输出型参数,保存读出的内容;count读取的大小
返回值:成功返回读取成功的字节数,失败返回-1
1.4 代码示例:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define SIZE 128
int main()
{
//close(1);
int fd = open("log.txt",O_CREAT|O_APPEND|O_RDWR,0644); //打开文件
if(fd<0)
{
perror("open error!");
return 1;
}
// const char* str1="Hello World 2!\n";
// write(fd,str1,strlen(str1)); //写文件 1
char buf[SIZE]={0};
read(fd,buf,15); //读文件
printf("buf = %s",buf);
close(fd); //关闭文件
return 0;
}
运行结果:
写文件 1:
读文件 1:
2.open函数返回值
在认识返回值之前,先来认识一下两个概念: 系统调用
和 库函数
- 上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。
- open close read write lseek 都属于系统提供的接口,称之为系统调用接口。
2.1 文件描述符fd
- 通过对open函数的学习,我们知道了文件描述符就是一个小整数。
0 & 1 & 2
- Linux进程默认情况下会有3个缺省打开的文件描述符。
- 分别是标准输入
0
。物理设备:键盘 - 标准输出
1
。 物理设备:显示器 - 标准错误
2
。 物理设备:显示器
打开文件操作需要进程调用open系统调用,因此必须让进程和文件关联起来,每一个进程都由一个file* 指针指向一张表files_struct,而这个结构体中最重要的一部分就是一个结构体指针数组file* fd_array[],这个数组中存放了当前进程所访问的所有文件,而文件描述符就是这个数组的下标。
2.2文件描述符的分配原则
- 在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。一般文件描述符从3开始,因为0,1,2 都被占用了。在每个文件打开时 0,1,2被占用了。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
3.重定向
- 本来应该输出到显示器上的内容,输出到了文件当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有: >, >>, <。
代码示例:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define SIZE 128
int main()
{
close(1);
int fd = open("log.txt",O_CREAT|O_TRUNC|O_RDWR,0644);
if(fd<0)
{
perror("open error!");
return 1;
}
const char* str="Hello World 1!\n";
const char* str1="Hello World 2!\n";
const char* str2="Hello World 3!\n";
write(1,str,strlen(str));
printf(str1);
fprintf(stdout,str2);
fflush(stdout);
close(fd);
return 0;
}
由运行结果可得,原本应该输入到显示器上的 Hello World 1!,Hello World 2!,Hello World 3!在程序运行以后,并没有输出出来,那去哪里了呢
?
- 因为close(1),在files_struct中下标 1 代表
标准输出1
关闭了1以后按照文件描述符的分配原则可知,fd此时为1,则将本来应该输出到显示器上的内容输出到了文件log.txt中。看下图查看log.txt中的内容证明:
那么什么是重定向的本质呢?
- 由上面 close(1)的方式实现重定向难免太挫,这时候就要来把
dup2()
介绍出来了。
3.1 dup2()
int dup2(int oldfd, int newfd)
;
头文件:#include <unistd.h>
- dup2可以用参数newfd指定新文件描述符的数值 若参数newfd已经被程序使用,则系统就会将newfd所指的文件关闭。
- 若newfd等于oldfd,则返回newfd,而不关闭newfd所指的文件。
- dup2所复制的文件描述符与原来的文件描述符共享各种文件状态。共享所有的锁定,读写位置和各项权限或flags等。
代码示例:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define SIZE 128
int main()
{
int fd = open("log.txt",O_CREAT|O_TRUNC|O_RDWR,0644);
if(fd<0)
{
perror("open error!");
return 1;
}
const char* str1="Hello World 2!\n";
write(fd,str1,strlen(str1));
dup2(fd,1);
printf(str1);
fflush(stdout);
close(fd);
return 0;
}
运行结果:
由上图两个运行结果可得和上面的close(1)重定向的效果一摸一样。
3.2 dup2()原理
- 当我们调用它的时候,dup会返回一个新的描述符,这个描述一定是当前可用文件描述符中的最小值。我们知道,一般的0,1,2描述符分别被标准输入、输出、错误占用,所以在程序中如果close掉标准输出1后,调用dup函数,此时返回的描述符就是1。对于dup2,可以
用fd2指定新描述符的值,如果fd2本身已经打开了,则会先将其关闭。如果fd等于fd2,则返回fd2,并不关闭它
。这两个函数返回的描述符与fd描述符所指向的文件共享同一文件表项。相当于把原本1的位置用新的文件覆盖掉。则下标1此时就指向新文件
。如下图所示: