系统I/O

共享文件
内核使用三个相关的数据结构来表示打开的文件

1.描述符表:每个进程都有独立的描述符表。他的表项是由进程打开的文件描述符索引的,每个打开的文件描述符指向文件表当中的一项。
2.文件表:打开的文件集合是由一张文件表来表示的所有的进程共享这张表。每个文件表的表项的组成:当前文件的位置,引用计数,一个指向v-node表中对应表项的指针。关闭一个描述符会减少相应文件表表项当中的引用计数,直到这个引用计数为0内核才会删除这个表项。
3.v-node表:所有的进程共享这张表。每个表项包含stat结构当中的大多数信息 包括st_size:包含文件的字节数的大小,st_mode编码了文件访问许可和文件类型。
例子:

#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("%d\n",getpid());
	printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
    } else {
	/* Child */
	sleep(1-s);
	Read(fd1, &c2, 1);
	printf("%d\n",getpid());
	printf("Child: c1 = %c, c2 = %c\n", c1, c2);
    }
    return 0;
}

在这里插入图片描述
分析:父进程中fork返回子进程的pid(非零),子进程中fork返回0.在调用子进程之前,父进程已经从 abcde.txt 读了一个字符 即c1=a。sleep函数可以将一个进程挂起一段指定的时间,若为sleep(1),该进程后执行;sleep(0)不影响进程执行,当父进程ID为偶数时,父进程中是sleep(0),子进程中是sleep(1)。因此父进程先执行,此时读c2,当前光标在a后面,所有读下一个字符 即c2=b;接下来执行子进程,因为这是父子进程共享文件位置,所以读子进程c2时,光标是在b后面的,因此c2=c (如第一次运行结果所示)

I/O重定向
下面来讨论一下I/O重定向的问题。
首先,学习一下dup()和dup2()重定向函数,这两个函数提供了复制文件描述符的功能。
#include
int dup(int oldfd);
int dup2(int oldfd, int newfd);
这两个函数调用成功之后,都返回一个oldfd文件描述符的副本,失败则返回-1。所不同的是,由dup()函数返回的文件描述符是当前可用文件描述符中的最小值,而dup2()函数则可以利用参数newfd指定要返回的文件描述符。如果参数newfd指定的文件描述符已经打开,系统想将其关闭,然后将oldfd指定的文件描述符复制到该参数。如果newfd等于oldfd,则dup2返回newfd,而不关闭它。
例子1:

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

在这里插入图片描述
在这里插入图片描述
分析:首先fd1 打开abcde.txt文件,有则打开并清空,无则创建;使用权限为可读可写。调用Write此时文件内容为pqrs。然后fd3打开文件 ,在文件尾追加内容。调用Write此时文件内容为pqrsjklmn。接下来将fd2重定向到fd1位置,fd1的当前光标位置在s后面。调用Write,会覆盖fd3一部分内容,此时文件内容为pqrswyzxn。最后调用Write在文件为追加内容,此时文件内容为pqrswyzxnef。

例子2:

#include "csapp.h"

int main(int argc, char *argv[])//abcde.txt
{
    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、fd2、fd3通过不同的文件表表项来引用同一个文件,每个描述符都有它自己的文件位置,所以对不同的描述符的读操作可以从文件不同的位置获取数据。而dup2(fd2, fd3);使fd2覆盖fd3的内容,此时fd2、fd3是同一个文件表表项。fd1读c1=a;fd2读c2=a,fd3此时与fd2光标位置一样都在a后面,使用fd3读c3=b。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值