CSAPP第10章 系统级I/O 学习笔记

这篇博客主要介绍了UNIX系统中I/O操作的概念,包括文件的打开、关闭、读写,以及使用RIO包进行高效读写。此外,还讨论了文件的共享和I/O重定向,强调了描述符在进程中的作用以及其对并发操作的影响。
摘要由CSDN通过智能技术生成

这节内容学起来比前面轻松很多,这里笔记尽量少讲一些观念的东西,尽量搞源码。

UNIX I/O

在UNIX系统中有一个说法,一切皆文件。所有的I/O设备,如网络、磁盘都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。这种将设备映射为文件的方式,允许UNIX内核引出一个简单、低级的应用接口,称为UNIX I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行。

1.打开文件
内核返回一个小的非负整数,叫做描述符
等于内核分配一个文件名,来标示当前的文件。
内核记录有关这个打开文件的所有信息。应用程序只需要记住标示符。
Unix外壳创建进程时都有三个打开的文件

标准输入(标示符0)
标准输出(标示符1)
标准错误(标示符2)
头文件<unistd.h>定义了常量代替显式的描述符值
STDIN_FILENO
STDOUT_FILENO
STDERR_FILENO

2.改变当前的文件位置
文件位置是从文件开头起始的字节偏移量,表示上次读到的位置。
3.读写文件
4.关闭文件
无论进程以何种原因终止,内核都会关闭所有打开的文件并释放它们的存储器资源。

打开和关闭文件

1.打开文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(char *filename,int flags,mode_t mode);

返回:若返回成功则为新文件描述符<这个是从零开始的,每打开一个文件就递增这个文件描述符,Linux shell每个进程开始时都会打开stdin(0),stdout(1),stderr(2)
flags(指明进程如何访问这个文件):

O_RNONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:如果文件不存在,就创建它的一个截断的(truncated)空文件。
O_TRYBC:如果文件已经存在,就截断它。
O_APPEND:在每次写操作前,设置文件位置到文件的结尾处。

mode(制定了新文件的访问权限位)<书P625>

2.关闭文件

#include <unistd.h>
int close(int fd);

若成功返回0,失败则返回-1.

读和写文件

#include <unistd.h>
ssize_t read(int fd,void *buf,size_t n);
//若成功则返回读的字节数,若EOF则为0,出错则为1
ssize_t write(int fd,const void *buf,size_t n);
//若成功则返回写的字节数,若出错则为-1

读写时要注意的是需要判断是否读完了。看例子:

/* $begin cpstdin */
#include "csapp.c"

int main(void) 
{
   
    char c;

    while(Read(STDIN_FILENO, &c, 1) != 0) 
		Write(STDOUT_FILENO, &c, 1);
    exit(0);
}
/* $end cpstdin */

这是从终端中一个个字符地读入,直到读到EOF为止。
结果就是
在这里插入图片描述
为了追踪程序中函数的调用,这边用一个强大的strace来跟踪,在终端中输入strace ./cpstdin即可。
在这里插入图片描述
由于这样是会输出所有的函数过程,看着很乱,可以用strace -e trace=read,write ./cpstdin来追踪read和write函数。
在这里插入图片描述
这样可以清晰的看到,读取abc和’\n’,write在终端上。

这样做虽然能够实现想要的功能,但是开销太大。因为每读一个字符都要调用系统级函数readwrite,系统级调用的开销比较大,因为将操作的过程全抛给操作系统 执行上下文切换 等等工作。
这个过程通常要20000到40000个时钟周期,可以说是开销巨大。
因而后面尽量一整块数据一起读来改进效率。

在网络通信中,如果网络延迟较大或者数据太大,经常不能一次就成功读写
完所有的数据,因而确保可靠,必须用while循环来保证完全读写。下面我
们将一下健壮的I/O包。

用RIO包健壮地读写

无缓冲的输入输出指的是直接在内存和文件之间传送数据,有缓冲的输入函数值的是输入的数据先保存在读缓冲区中,再从读缓冲区读入内存。直接在csapp.c文件中看源码吧。
1.RIO的无缓冲的输入输出函数

#include "csapp.h"

ssize_t rio_readn(int fd,void *usrbuf,size_t n);
ssize_t rio_writen(int fd,void *usrbuf,size_t n);
// 参数以及返回值的意义和read,write的一样

下面来看rio_readn的源码吧

// rio_readn - robustly read n bytes (unbuffered)
/* $begin rio_readn */
ssize_t rio_readn(int fd, void *usrbuf, size_t n) 
{
   
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;

    while (nleft > 0) {
   
	if ((nread = read(fd, bufp, nleft)) < 0) {
   
	    if (errno == EINTR) /* interrupted by sig handler return */
		nread = 0;      /* and call read() again */
	    else
		return -1;      /* errno set by read() */ 
	} 
	else if (nread == 0)
	    break;              /* EOF */
	nleft -= nread;
	bufp += nread;
    }
    return (n - nleft);         /* return >= 0 */
}

/* $end rio_readn */

如上代码所示,无缓冲的输入函数rio_readn每次都会要求系统读取剩余的所有数据,但是可能没有成功读取所有嘛,所有用一个while循环,表示只有要求的字节数都读了或者遇到EOF才停止。
下面来看rio_writen的源码吧!

/*
 * rio_writen - robustly write n bytes (unbuffered)
 */
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值