Linux基础IO

目录

回顾C语言IO操作

默认会打开的三个输入输出流

认识系统IO 

open

open函数的第一个参数是pathname,表示要打开或创建的目标文件

open函数的第二个参数是flags,表示打开文件的方式 

open函数的第三个参数是mode,表示创建文件的默认权限

open的返回值

close

write 

read

文件描述符fd

重定向

输出重定向

输入重定向

追加重定向

 标准输出流和标准错误流对应的都是显示器,它们有什么区别?

重定向函数dup2

FILE

如何理解缓冲区? 


回顾C语言IO操作

默认会打开的三个输入输出流

在C语言中 stdin, stdout, stderr

在C++中  cin,cout,cerr

其中

stdin(cin)——标准输入流  

stdout(cout)——标准输出流

stderr(cerr)——标准错误流

 在Linux中一切皆文件,stdin(cin)对应的就是键盘对应的文件

stdout(cout)和stderr(cerr)对应的就是显示器对应的文件

并且都是FILE*类型

认识系统IO 

每一种语言都有自己对应的文件操作函数,如打开文件,关闭文件等,但这些接口的本质,还是对系统IO接口的封装

open

open函数的第一个参数是pathname,表示要打开或创建的目标文件

若pathname以路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建。

若pathname以文件名的方式给出,则当需要创建该文件时,默认在当前路径下进行创建。

open函数的第二个参数是flags,表示打开文件的方式 

 常用选项

open函数的第三个参数是mode,表示创建文件的默认权限

注意:这里的默认权限会受到umask权限掩码的影响(你所看到的权限掩码=mode&(~umask)),所以在使用之前要把umask设置 设置为0

umask(0)

open的返回值

open的返回值为新打开文件的文件描述符fd

从结果可以看到文描述符是从3开始递增的

打开不存在的文件

close

int close(int fd);

close函数时传入需要关闭文件的文件描述符,关闭文件成功返回0,关闭文件失败返回-1。

write 

ssize_t write(int fd, const void *buf, size_t count);

将buf位置开始向后count字节的数据写入文件描述符为fd的文件当中 


	int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
	if (fd < 0){
		perror("open");
		return 1;
	}
	const char* msg = "hello world\n";
	for (int i = 0; i < 5; i++){
		write(fd, msg, strlen(msg));
	}

数据写入成功,返回实际写入数据的字节个数;数据写入失败,返回-1

read

ssize_t read(int fd, void *buf, size_t count);

从文件描述符为fd的文件读取count字节的数据到buf位置当中 

数据读取成功,返回实际读取数据的字节个数;数据读取失败,返回-1

文件描述符fd

Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.

0,1,2对应的物理设备一般是:键盘,显示器,显示器

 

关闭文件描述符0

这里直接给出结论: 

进程中,文件描述符的分配规则:在文件描述符表中,最小的,没有被使用的数组元素,分配给新文件

重定向

原理:在上层无法感知的情况下,在OS内部更改进程对应的文件描述符表中特定下标的指向

输出重定向

输出重定向就是,将我们本应该输出到一个文件的数据重定向输出到另一个文件中

在打开文件之前关闭stdout的文件描述符,然后打开文件,那么这个新打开的文件描述符就是1

而系统默认会打印内容到显示器也就是文件描述符为1的显示器文件中 

 

输入重定向

这里也是一样,事先将数据放要打开的文件中,在关闭0号文描述符后打开文件,键盘文件读取会到log.txt中

追加重定向

追加重定向和输出重定向的唯一区别就是,输出重定向是覆盖式输出数据,而追加重定向是追加式输出数据

 标准输出流和标准错误流对应的都是显示器,它们有什么区别?

重定向的是文件描述符是1的标准输出流,而并不会对文件描述符是2的标准错误流进行重定向。 

重定向函数dup2

int dup2(int oldfd, int newfd);

 从英文描述上看很容易会认为是将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中

如果要将1文件描述符的内容指向fd指向的文件该怎么用呢?dup2(1,fd)还是dup2(fd,1)

不要认为fd指向的文件是新打开的 就是newfd的位置,这里要看函数本身的用法

答案这里这里的正确用法是 dup(fd,1)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
	int fd = open("log.txt", O_WRONLY | O_CREAT|O_APPEND, 0666);
	if (fd < 0){
		perror("open");
		return 1;
	}
	close(1);
	dup2(fd, 1);
	printf("hello world\n");
	return 0;
}

FILE

因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访 问的。

所以C库当中的FILE结构体内部,必定封装了fd。

如何理解缓冲区? 

下面看一个例子

在显示器上打印了两行信息,但是重定向到log.txt中有三行

C语言除了要在struct FILE封装fd 还要在结构体内预留一部分缓冲区,所以当fprintf使用的时候,是将数据先放到了缓冲区中,然后这个函数就会直接返回,接下来C标准库会结合一定的刷新策略将缓冲区中的数据写入给OS(write) 

首先我们应该知道的是,缓冲的方式有以下三种:

  1. 无缓冲。(不提供缓冲,直接给OS)
  2. 行缓冲。(常见的对显示器进行刷新数据)
  3. 全缓冲。(常见的对磁盘文件写入数据,缓冲区写满才刷新)

这个缓冲区在哪里?—— 在FILE结构体当中

为什么这里文件里打印了两次fprintf打印了两次?

当我们直接执行可执行程序,将数据打印到显示器时所采用的就是行缓冲,因为代码当中每句话后面都有\n,所以当我们执行完对应代码后就立即将数据刷新到了显示器上。
而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fputs函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和puts函数打印的数据就有两份。但由于write函数是系统接口,我们可以将write函数看作是没有缓冲区的,因此write函数打印的数据就只打印了一份

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷帅且洋仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值