Linux:基础IO

回顾C文件接口

stdin & stdout & stderr

C 默认会打开三个输入输出流,分别是 stdin, stdout, stderr
仔细观察发现,这三个流的类型都是 FILE*, fopen 返回值类型,文件指针

系统文件I/O

接口介绍

open

man 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 : 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数 :
O_RDONLY: 只读打开
O_WRONLY : 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND : 追加写

O_TRUNC:打开文件时会清空文件内容
返回值:
成功:新打开的文件描述符
失败: - 1 

mode_t 理解:直接 man 手册,比什么都清楚。
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要 open 创建,则第三个参数表示创建文件的默认权限, 否则,使用两个参数的 open
hello.c 写文件 :
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
	umask(0);
	int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	int count = 5;
	const char* msg = "hello bit!\n";
	int len = strlen(msg);
	while (count--) {
		write(fd, msg, len);//fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数据。 返回值:实际写了多少字节数据
		//默认不会清空文件内容,从头开始写
	}
	close(fd);
	return 0;
}
hello.c 读文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
	int fd = open("myfile", O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	const char* msg = "hello bit!\n";
	char buf[1024];
	while (1) {
		ssize_t s = read(fd, buf, strlen(msg));//类比write
		if (s > 0) {
			printf("%s", buf);
		}
		else {
			break;
		}
	}
	close(fd);
	return 0;
}

open函数返回值

在认识返回值之前,先来认识一下两个概念 : 系统调用 库函数
上面的 fopen fclose fread fwrite 都是 C 标准库当中的函数,我们称之为库函数( libc )。
而  open close read write lseek 都属于系统提供的接口,称之为系统调用接口
回忆一下我们讲操作系统概念时,画的一张图
系统调用接口和库函数的关系,一目了然。
所以,可以认为, f# 系列的函数,都是对系统调用的封装,方便二次开发。

文件描述符fd

通过对 open 函数的学习,我们知道了文件描述符就是一个小整数
文件描述符fd的本质是内核的进程中文件映射关系的数组的下标
理解:在进程中每打开一个文件,都会创建有相应的文件描述信息struct file,这个描述信息被添加在pcb的struct files_struct中,以数组的形式进行管理,随即向用户返回数组的下标作为文件描述符,用于操作文件

 0 & 1 & 2

Linux 进程默认情况下会有 3 个缺省打开的文件描述符,分别是标准输入 0 , 标准输出 1, 标准错误 2.
0,1,2 对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
	char buf[1024];
	ssize_t s = read(0, buf, sizeof(buf));
	if (s > 0) {
		buf[s] = 0;
		write(1, buf, strlen(buf));
		write(2, buf, strlen(buf));
	}
	return 0;
}

而现在知道,文件描述符就是从 0 开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表 files_struct, 该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

文件描述符的分配规则

直接看代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.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;
}
输出发现是 fd: 3
关闭 0 或者 2 ,在看
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	close(0);
	//close(2);
	int fd = open("myfile", O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}
发现是结果是: fd: 0 或者 (//) fd:2 可见,文件描述符的分配规则:在 files_struct 数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

用库函数而不用系统调用的原因

系统调用:代码不具备跨平台性

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-03-28 20:02:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-28 20:02:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-28 20:02:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-28 20:02:03       18 阅读

热门阅读

  1. 自定义对外开放接口的加解密和签名、验签

    2024-03-28 20:02:03       17 阅读
  2. docker的dockerfile

    2024-03-28 20:02:03       18 阅读
  3. 使用BenchmarkSQL压测openGauss

    2024-03-28 20:02:03       21 阅读
  4. 线程通信同步

    2024-03-28 20:02:03       15 阅读
  5. 03 React 基础样式控制

    2024-03-28 20:02:03       15 阅读
  6. gite 实战教程

    2024-03-28 20:02:03       21 阅读