系统级IO

UNIX I/O

Everything is a file , in Unix
所有的IO设备(例如网络,磁盘和终端)都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行

UNIX I/O下输入输出统一的方式

1.打开文件
2.Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(fd=0),标准输出(fd=0),标准错误(fd=2)
2.改变当前文件的位置
3.读写文件
4.关闭文件

打开文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int open (char *filename,int flags,mode_t mode);
关闭文件
#include<unstd.h>

int close(int fd);
读和写文件
#include<unstd.h>
ssize_t read(int fd,void *filename,size_t n);
ssize_t write(int fd,const void *buf,size_t n);

读:从文件读到内存
写:从内存写到文件

读取文件数据
#include<unstd.h>
#include<sys/stat.h>
#include<sys/types.h>

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

I/O重定向

#include<unistd.h>

int dup2(int oldfd,int newfd);

该函数的作用是用oldfd所指的文件内容去覆盖newfd所指的文件的内容。
若newfd已经打开了,dup2会在复制oldfd之前关闭newfd。功能如下图:
在这里插入图片描述

共享文件

描述符表: 每个进程有独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的文件描述符表项指向文件表中的一个表项
文件表: 打开文件的集合是由一张文件表来表示的,所有的进程共享这张表。
v-node表: 所有进程共享这张v-node表,每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员
(以下图片来源深入理解计算机系统第三版)
无文件共享的情况:
在这里插入图片描述
有文件共享的情况:
在这里插入图片描述
父子进程共享文件的情况:
在这里插入图片描述

系统IO的举例

注意:以下代码中的打开关闭或者读写文件的函数(首字母大写的函数)不是系统定义的,而是在csapp.h头文件中自定义的包含出错处理的函数

abcde.txt的内容为:
abcde
练习1

#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;
}

dup2(fd2,fd3)是用fd2所指文件的内容覆盖fd3所指文件的内容,所以当fd2被读取一个字符(文本的第一个字符)之后,到fd3读取一个字符读的就是fd2读取之后的下一个字符,即文本的第二个字符,而fd1与fd2,fd3无关,所以其读取的字符就是文本的第一个字符。
运行结果如下:
在这里插入图片描述

练习2

#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;
}

sleep(time)函数来睡眠time秒,第一个sleep函数是为了让父进程先运行,第二次使用sleep是为了运行子进程,由于子进程继承的是父进程打开的文件,所以当父进程读取一个字符之后子进程读取的就是下一个字符
在这里插入图片描述
练习3

#include "csapp.h"

int main(int argc, char *argv[])
{
    int fd1, fd2, fd3;
    char *fname = argv[1];
    fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
    Write(fd1, "pqrs", 4);	

    fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
    Write(fd3, "jklmn", 5);
    fd2 = dup(fd1);  /* Allocates new descriptor */
    Write(fd2, "wxyz", 4);
    Write(fd3, "ef", 2);

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

/*abcde.txt
pqrswxyzef
*/

如何访问文件的一些参数:
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:如果文件不存在就创建它的一个空文件
O_TRUNC:如果文件已经存在则截断它,即覆盖原有文件的内容
O_APPEND:追加,即写在文件原有内容的结尾处
fd1,fd2,fd3都是对同一个文件进行操作
fd1文件写“pqrs”的时候是以截断的方式,所以覆盖了文本文件中原有的内容“abcde”—文件内容为:pqrs
fd3文件写“jklmn”的时候是以追加的方式,所以文件原有的内容不变,只增加了几个字符—文件内容为:pqrsjklmn
fd2重定向到fd1所指的文件表(fd2=dup(fd1)),对fd2进行操作实际是对fd1进行操作,fd2四个字符覆盖了fd3添加的内容的前4个字符—文件内容为:pqrswxyzn
fd3以追加的方式在文件末尾又添加了两个字符—文件内容为:pqrswxyznef
在这里插入图片描述
修改过后的abcde.txt
在这里插入图片描述
练习4

/* $begin statcheck */
#include "csapp.h"

```c

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);
}

该部分展示了如何使用宏和stat函数来读取和解释一个文件的st_mode位
Linux在sys/stat.h中定义了宏谓词来确定st_mode成员的文件类型,如下:
S_ISREG(m):是否是普通文件
S_ISDIR(m):是否是目录文件
S_ISSOCK(M):是否是网络套接字
在这里插入图片描述
计算机系统基础习题部分
foobar.txt的内容为:
foobar
练习题10.2

#include"csapp.h"
int main()
{
 int fd1,fd2;
 char c;
 
 fd1=Open("foobar.txt",O_RDONLY,0);
 fd2=Open("foobar.txt",O_RDONLY,0);
 Read(fd1,&c,1);
 Read(fd2,&c,1);
 printf("c=%c\n",c);
 exit(0);
 }

fd1和fd2打开文件并没有关联,重新打开一个文件光标都在开头,所以二者读到的字符都是文件的第一个字符:f
在这里插入图片描述
练习题10.3

#include"csapp.h"
int main()
{
 int fd;
 char c;
 
 fd=Open("foobar.txt",O_RDONLY,0);
 if(Fork()==0){
   Read(fd,&c,1);
   exit(0);
   }
 Wait(NULL);
 Read(fd,&c,1);
 printf("c=%c\n",c);
 exit(0);
 }

父子进程共享文件,子进程读完之后光标移到下一个字符处,父进程再读读的就是第二个字符
在这里插入图片描述
练习题10.5

#include"csapp.h"
int main()
{
 int fd1,fd2;
 char c;
 
 fd1=Open("foobar.txt",O_RDONLY,0);
 fd2=Open("foobar.txt",O_RDONLY,0);
 Read(fd2,&c,1);
 Dup2(fd2,fd1);
 Read(fd1,&c,1);
 printf("c=%c\n",c);
 exit(0);
 }

首先读取了fd2的第一个字符,接着fd1被重定向到fd2,再读fd1实际读的就是fd2,所以最终读取的是fd2的第二个字符
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值