csapp笔记——系统级I/O

本文详细介绍了Unix系统中的I/O操作,包括文件描述符的概念,打开和关闭文件的函数open和close,读写文件的read和write函数,以及如何获取文件元数据。此外,还讨论了文件的共享、子进程如何继承文件描述符,并通过示例解释了I/O重定向的实现。
摘要由CSDN通过智能技术生成

Unix I/O

一切皆文件==
所有的I/O设备都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅的映射为文件的方式,允许linux内核引出一个简单、低级的应用接口,称为Unix I/O.
一个应应用程序通过要求内核打开相应文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符。
Linux shell创建的每个进程开始都有三个打开的文件:
标准输入(描述符为0)、标准输出(描述符为1)、标准错误(描述符为2)
头文件<unistd.h>定义了常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO用他们来代替描述符值。

打开和关闭文件

用open函数打开一个已经存在的文件或创建一个新文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char* filename,int flags, mode_t mode);

open函数将filename转换为一个文件描述符,并且返回描述符数字。返回的描述符总是进程中当前没有打开的最小描述符。又shell已经打开了3个标准文件故描述最小为3.

函数的3个参数
filename文件名

flags参数指明了打开文件的方式:

O_RDONLY:只读

O_WRONLY: 只写

O_RDWR: 可读可写

O_CREAT: 文件不存在,就创建一个它的截断的空文件。(就是创建一个新空白文件)

O_TRUNC: 如果文件已经存在,就截断它。(清空它)

O_APPEND: 在每次写操作前,设置文件位置到文件结尾处。

mode参数指定了访问权限:

S_IRUSR:使用者(拥有者)能够读这个文件

S_IWUSR:使用者(拥有者)能够写这个文件

S_IXUSR:使用者(拥有者)能够执行这个文件

S_IRGRP:拥有者所在组的成员能够读这个文件

S_IWGRP:拥有者所在组的成员能够写这个文件

S_IXGRP:拥有者所在组的成员能够执行这个文件

S_IROTH:其他人(任何人)能够读这个文件

S_IWOTH:其他人(任何人)能够写这个文件

S_IXOTH:其他人(任何人)能够执行这个文件
一般S_IRUSR、S_IWUSR、S_IXUSR为4、2、1可用这几个数字相加来组合权限,如6表示使用者可读可写这个文件
用close函数关闭一个打开的文件

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

读和写文件

#include <unistd.h>
ssize_t read(int fd,void *buf,size_t n);
ssize_t write(int fd,const void *buf,size_t n);

read函数:fd表示文件描述符 buf指向一段内存空间表示读到哪去 size表示读多少字节
write函数从内存buf出至多复制n个字节到描述符为fd的当前文件位置。

读取文件元数据

应用程序能够通过调用stat和fstat函数,检索到关于文件的信息(有时也称为文件的元数据)

#include <unistd.h>
#include <sys/stat.h>

int stat(const char*filename,struct stat* buf);
int fstat(int fd,struct stat* buf);

stat函数以一个文件名为输入,可以查看到文件的各种数据,fstat函数是相似的。
在这里插入图片描述
st_mode成员的文件类型:
S_ISREG(m)。这是一个普通文件吗?
S_ISDIR(m)。这是一个目录文件吗?
S_ISSOCK(m)。这是一个网络套接字吗?
下面程序查询和处理一个文件的st_mode位:

int main (int argc, char **argv) 
{
    struct stat stat;
    char *type, *readok;

    /* $end statcheck */
    if (argc != 2) {
	fprintf(stderr, "usage: %s <filename>\n", argv[0]);
	exit(0);
    }
    /* $begin statcheck */
    Stat(argv[1], &stat);
    if (S_ISREG(stat.st_mode))     /* Determine file type */
	type = "regular";
    else if (S_ISDIR(stat.st_mode))
	type = "directory";
    else 
	type = "other";
    if ((stat.st_mode & S_IRUSR)) /* Check read access */
	readok = "yes";
    else
	readok = "no";

    printf("type: %s, read: %s\n", type, readok);
    exit(0);
}

在这里插入图片描述

共享文件

内核用下面三个相关的数据结构来表示打开的文件:
描述符表
每个进程都有独立的描述符表,表项是由进程打开的文件描述符来索引的。每个描述符表项只想文件表中的一个表项。
文件表
打开文件的集合是由一张文件表来表示的,所有进程共享。它记录了当前文件的位置,当前指向该表项的描述符表项数(成为引用计数)和一个指向v-node表中对应表项的指针。当引用计数为0是,内核会自动删除这个文件表表项。
v-node表
所有进程共享,包含了stat结构中的大多数信息。
两个描述符引用不同的文件。没有共享
在这里插入图片描述
两个描述符通过两个打开文件表表项共享同一个磁盘文件
在这里插入图片描述

子进程继承父进程的打开文件(初始状态如上上图)
在这里插入图片描述
看代码:

#include "csapp.h"

int main(int argc, char *argv[])
{
    int fd1;
    int s = getpid() & 0x1;
    char c1, c2;
    char *fname = argv[1];
    fd1 = Open(fname, O_RDONLY, 0);
    Read(fd1, &c1, 1);
    if (fork()) {
	/* Parent */
	sleep(s);
	Read(fd1, &c2, 1);
	printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
    } else {
	/* Child */
	sleep(1-s);
	Read(fd1, &c2, 1);
	printf("Child: c1 = %c, c2 = %c\n", c1, c2);
    }
    return 0;
}

运行结果
在这里插入图片描述
我们看到了:
1.从abcde.txt,并且读取了一个字符,现在光标停留在了a之后
2.进行了fork,子进程复制了父进程的环境。
3.然后两个进程都遇到了sleep,接着先继续执行子进程,它读了一个字符,因为先前光标停留在ab之间,所以此时c2读到的是b
4.child执行完后回到父进程,父进程和子进程的文件表表项相同,所以光标在b后,往后读一个,读到了c

I/O重定向

Linux shell提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。如:
linux >ls>foo.text使得shell加载ls程序,将标准输出重定向到foo.text.
如何重定向:
使用dup2函数

#include <unistd.h>
int dup2(int oldfd,int newfd);

dup2函数复制描述符表表项oldfd到描述符表项newfd,覆盖描述符表项newfd以前的内容。如果newfs已经带开了,dup2会在复制oidfd之前关闭newfd.
看代码示例;

#include "csapp.h"

int main(int argc, char *argv[])
{
    int fd1, fd2, fd3;
    char c1, c2, c3;
    char *fname = argv[1];
    fd1 = Open(fname, O_RDONLY, 0);
    fd2 = Open(fname, O_RDONLY, 0);
    fd3 = Open(fname, O_RDONLY, 0);
    dup2(fd2, fd3);

    Read(fd1, &c1, 1);
    Read(fd2, &c2, 1);
    Read(fd3, &c3, 1);
    printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);

    Close(fd1);
    Close(fd2);
    Close(fd3);
    return 0;
}

运行结果;
在这里插入图片描述
fd1正常读写abcde.text的内容读到a;
fd2也正常读写abcde.text的内容读到a,光标在ab间,
fd3重定位到fd2顺着继续读写一个字符得到b。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值