【Linux】系统I/O调用和文件描述符

一. 系统调用 I/O

1. open()

  • 打开或者创建一个文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
  • 参数:
    pathname: 打开或者创建文件的路径, 若只有文件名则默认在当前路径下.
    flags: 代表文件打开方式.
    mode: 创建文件时, 创建文件的权限.

  • 返回值:
    若打开或者创建成功, 返回一个整型值 文件描述符 (file descriptor); 若失败, 返回 -1 并设置错误码.

flags

flags 也被称作标记位, 和 waitpid() 的 status 参数一样, 都是通过位图传参, 按照比特位来区分打开方式.

参数含义
O_RDONLY以只读的方式打开文件
O_WRNOLY以只写的方式打开文件
O_APPEND以追加的方式打开文件
O_RDWR以读写的方式打开文件
O_CREAT当目标文件不存在时,创建文件

例:

#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <stdio.h>    
#include <unistd.h>    
#include <string.h>    
    
int main()    
{    
  int fd = open("test.c", O_RDONLY); //以只读方式打开 test.c 文件    
  if (fd < 0)    
    return 1;  
    
  char s[1024];    
  ssize_t n = read(fd, s, 1024);         //读取内容            
  write(1, s, n);    //打印内容                                                                                     
  
  return 0;    
}   

在这里插入图片描述

mode

使用 open() 函数创建文件时, 必须指明文件权限. 所以当 文件打开方式包含创建文件时, 需要设置mode参数.

例:

#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <stdio.h>    
#include <unistd.h>    
    
int main()    
{    
  umask(0); //权限掩码置0    
  int fd = open("log.txt", O_WRONLY | O_CREAT, 0666); //以只写方式打开 test.c 文件, 若文件不存在则创建文件                                                               
  if (fd < 0)    
    return 1;        
  return 0;    
}  

在这里插入图片描述

2. close()

  • 关闭一个文件描述符
#include <unistd.h>

int close(int fd);
  • 参数 fd: open() 函数的返回值, 文件描述符.

例:

#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <stdio.h>    
#include <unistd.h>    
    
int main()    
{    
  umask(0); //权限掩码置0    
  int fd = open("log.txt", O_WRONLY | O_CREAT, 0666); //以只写方式打开 test.c 文件, 若文件不存在则创建文件       
  if (fd < 0)    
    return 1;                                                         
  close(fd);	//关闭文件
  return 0;    
}  

3. write()

向文件描述符指向的文件中写入.

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
  • 参数
    fd: open() 函数的返回值, 文件描述符.
    buf: 要写入的缓冲区.
    count: 需要写入的字节数.

  • 返回值:
    若写入成功, 返回写入数据的字节个数; 若失败, 返回 -1 并设置错误码.

#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <stdio.h>    
#include <unistd.h>   
#include <string.h>    
    
int main()    
{    
  umask(0); //权限掩码置0    
  int fd = open("log.txt", O_WRONLY | O_CREAT, 0666); //以只写方式打开 test.c 文件, 若文件不存在则创建文件
  if (fd < 0)    
    return 1;    
    
    
  char s[] = "11111\n";    
  write(fd, s, strlen(s));
  close(fd);

  return 0;                                                                          
}  

在这里插入图片描述

4. read()

向文件描述符指向的文件中读取.

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
  • 参数
    fd: open() 函数的返回值, 文件描述符.
    buf: 保存读取内容的缓冲区.
    count: 请求读取的字节数.

  • 返回值
    若读取成功, 返回读取数据的字节个数; 若失败, 返回 -1 并设置错误码; 若在调 read() 之前已到达文件末尾, 则这次 read() 返回 0.

二. 文件描述符(fd)

1. 内存中的文件

在操作系统看来, 无论是在磁盘中存储的程序, 软件, 还是磁盘或键盘等硬件, 实际上都是文件, 不过文件的内容和属性, 操作方式不同.
当操作系统将文件加载至内存时, 文件就被描述为了包含类型, 状态等属性的 struct file 结构体.

//描述文件
struct file 
{
	//文件的各种属性
	int types;  //文件的类型
	int status;  //文件的状态
	int (*writep)(...);  //函数指针,指向读函数
	int (*readp)(...);	//函数指针,指向写函数
	struct file* next;  //下一个file对象的地址
	...
}

在这里插入图片描述

Linux 这种使用方法集屏蔽底层差异的 struct file 结构体这一层称作 虚拟文件系统 vfs, 使得文件在上层使用统一的文件接口的方式进行操作.

2. 文件描述符(fd)

为了使一个进程可以打开多个文件, 会在 task_struct 结构体中包含 struct files_struct *file 的指针, 指向当前进程的 struct files_struct 结构体, 在 files_struct 中的 file* fd_array 指针数组会包含当前进程打开或从父进程继承下来的文件的地址.

在这里插入图片描述

文件描述符(fd)正是 struct file *fd_array[] 的结构体指针数组的下标.
是 open() 函数调用成功的返回值, 所以使用系统的 I/O 接口都需要 fd 参数.

浅显理解为:
每当 open() 函数新打开文件时, 操作系统会创建新的 struct file 结构, 进行文件管理, 会在 struct file *fd_array[] 的数组中保存一份地址, 最后返回数组下标.

文件描述符(fd)默认是从 3 开始. 0, 1, 2下标的地址分别指向标准输入(键盘), 标准输出(显示器), 标准错误(显示器), 是系统默认打开的.
但若将关闭 0 后, 那么文件描述符(fd)的就会从 0 位置开始, 实际上文件描述符(fd)是从最小的, 没有存储信息的一位下标开始.

在这里插入图片描述

3. dup2()

#include <unistd.h>

int dup2(int oldfd, int newfd);

在这里插入图片描述
将 oldfd 拷贝给 newfd, 必要时会先关闭 newfd. 函数只会保留 oldfd 的值.

4. 重定向

进程在进行 I/O 的时, 默认只会根据标准输入(stdin), 标准输出(stdout), 标准错误(stderr)对应的文件描述符(fd)进行操作;
也就是进程只会寻找在 struct file *fd_array[] 指针数组中 0,1,2 下标位置存放的文件指针, 对该指针指向的文件进行输入, 输出操作; 上层所使用的 printf(), scanf() 等函数中也封装了文件描述符(fd).

重定向指 将 struct file *fd_array[] 指针数组中原有的文件指针替换, 在内核更改了数组下标指向的内容, 而上层依然使用原有的下标, 改变所操作的文件.

可以在文件打开之前将需要重定向的下标关闭, 再打开文件, 可以达到重定向的效果.
在这里插入图片描述

也可以使用 dup2() 函数实现重定向.
在这里插入图片描述

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值