基础IO与文件描述符

一、引题

       之前我们讲过C标准库提供的IO函数fread,fwrite等,那么它们到底是怎么实现的呢?它们是真的靠自己写出来的吗?不一定?

       我们先来看看关于操作系统的概念图




       从中我们可以看出C标准库属于用户部分,是给用户的操作接口,不过这个接口它又是来源于系统调用接口,可以看成他是系统调用的封装,那我们探究一下我们之前提到的c标准库给我们提供的函数fopen(),它封装了那个系统调用的那个接口呢?百度之后发现它其实封装的是open(),我们在Linux下man open,我们发现接口的原型有两种:

 int open(const char *pathname, int flags);

功能:打开和创建文件

参数:pathname:待打开/创建文件的路径名 flags:打开模式()

O_RDONLY只读模式

O_WRONLY只写模式

O_RDWR读写模式

以上三种常量必须选一个,下面的是非必须选,下面这些和上面这些或(|)起来构成完整的打开模式

O_APPEND每次写操作都写入文件的末尾

O_CREAT如果指定文件不存在,则创建这个文件

O_EXCL如果要创建的文件已存在,则返回-1,并且修改errno的值

O_TRUNC如果文件存在,并且以只写/读写方式打开,则清空文件全部内容(即将其长度截短为0)

O_NOCTTY如果路径名指向终端设备,不要把这个设备用作控制终端。

O_NONBLOCK如果路径名指向FIFO/块文件/字符文件,则把文件的打开和后继I/O

返回值:成功则返回文件描述符,否则返回-1

int open(const char *pathname, int flags, mode_t mode);

这个与上面基本一致,只是多了个标志位 这个标志位是用来创立新文件时候,给新文件定各个用户权限的

mode 是一个是一个无符号八进制数,为什么呢,这个就要和chmod联系起来,我们当初设置权限数字的每一位代表一类权限。用户所获得的权限是加权数值的总和。例如764表示所有者拥有读、写和执行权限,群组拥有读和写权限,其他用户拥有读权限。所以同样的,这里的mode也是同样的意思。


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

功能:从文件中读数据

参数:fd:文件描述符  buf:保存数据的起始地址  count:指定的字节数

返回值:成功返回读文档的字节数,失败返回-1,当读到EOF,返回0


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

功能:从打开文件中读数据

参数:fd:文件描述符  buf:指定缓冲区  count:指定的字节数

返回值:成功返回写文档的字节数,失败返回-1

By:注意write和read返回值类型是有符号长整型,这样才可以返回负数,达到报错的目的



二、文件描述符

运行环境:gcc (GCC) 4.4.7 

代码:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>


//fd连续分配
void Test1()
{
	umask(0);
	int fd1 = open("./myfile1",O_RDONLY|O_CREAT,0666);
	if(fd1==-1)
		printf("open Error\n");
    int fd2 = open("./myfile1",O_RDONLY|O_CREAT,0666);
    if(fd2==-1)
		printf("open Error\n");
    printf("fd1 = %d fd2 = %d\n",fd1,fd2);
    close(fd1);
    close(fd2);
}

//fd从最小的开始分配
void Test2()
{
	umask(0);
	int fd1 = open("./myfile1",O_RDONLY|O_CREAT,0666);
	if(fd1==-1)
		printf("open Error\n");


	printf("fd1 = %d\n",fd1);
	close(fd1);
    int fd2 = open("./myfile2",O_RDONLY|O_CREAT,0666);
    if(fd2==-1)
		printf("open Error\n");
    printf("fd2 = %d\n",fd2);
    close(fd2);
}

int main()
{
    //Test1();
    Test2();
    return 0;
}

Test1()运行结果:

fd1 = 3 fd2 = 4

Test2()运行结果:

fd1 = 3
fd2 = 3

       从这两个运行结果我们可以看出,fd按照从小到大依次分配下标,总是选择在当前情况下最小的一个下标进行分配,作为新的文件描述符,但是我们又有了新的问题,为什么从3开始分配呢?那0,1,2去干嘛了,经过查找,原来0,1,2分别对应着标准输入,标准输出,标准错误,Linux进程默认情况下会打开的文件描述符既然有了这个,那我们就可以利用这种特性改善过去的输出和输入


#include <stdio.h>
#include <fcntl.h>
#include <string.h>
int main()
{
	char* buf ="hello fd!";
	write(1, buf, strlen(buf));
	write(2, buf, strlen(buf));
	return 0;
}

运行结果:

hello fd!

    通过这里,我们发现利用这种文件描述符,也可以完成向屏幕输入和打印的功能

#include <stdio.h>
int main()
{
	close(1);
	int fd = open("./test.txt",O_RDONLY|O_CREAT);
	if(fd==-1)
	{
		printf("Error\n");
	}
	printf("fd = %d\n",fd);
	close(fd);
	return 0;
}:


运行结果:

fd = 1

       顺便补充下,当我们关闭0,依然符合我们之前总结的,从第一个空闲的最小的下标开始分配的规则。并且最为关键的是实现了重定向,这下相当于之前向屏幕输出的数据,以后都会输出到我们所创建的这个test.txt文件中

注意事项:

open()过后,一定要记得close(),否则会造成资源泄漏,为什么呢,这就需要谈到文件描述符,我们首先来看下面这张图,了解下fd存在意义



       我们将文件描述符简称为fd,从这张图,我们可以看出fd其实是fd_array这个数组的下标,在一般情况下,进程默认打开标准输入,标准输出,标准错误,它们的下标分别是0,1,2,之后的下标是为我们预留,供用户使用的,如果我们不close,那么fd_array数组就会使用完,导致资源泄露。



总结下:从这个文件描述符也让我们了解了Linux的重要思想,一切皆文件,不管是普通文件,目录,字符设备,块设备,套接字,在Linux中都是以文件被对待,它们虽然类型不同,但是对其提供的确实同一套操作模式





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值