csapp 系统级I/O

10 系统级I/O

10.1 unix I/O

1.一个linux文件是一个多个字节的序列。
2.所有的I/O设备都模型化为文件
3.所有的输入输出都变成文件的读写执行

  • 打开文件:内核打开文件返回一个小的非负整数作为文件描述符,后续所有的对此文件的操作都以这个描述符为标识。
  • linux shell创建进程时都有三个打开的文件
    标准输入(标示符0)
    标准输出(标示符1)
    标准错误(标示符2)
  • 改变文件位置:对每个打开的文件保持一个文件位置k,初始为0。文件位置即是从文件开头起始的字节偏移量
  • 读写文件:一个读操作就是从文件拷贝n>0个字节到存储器,然后将k增加到k+n。
    写操作就是从存储器拷贝n个字节到一个文件,从当前文件位置k开始,然后更新k。
  • 关闭文件:内核释放文件打开时创建的数据结构。将描述符恢复到可用的描述符池中。
10.2 文件
  • 普通文件
  • 目录
  • 套接字
10.3 打开和关闭文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

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

                        //返回:若成功则为新文件描述符,若出错为-1

  • filename就是文件名
  • flags:指明进程如何访问这文件。
    • O_RDONLY: 只读
    • O_WRONLY: 只写
      • O_CREAT : 如果文件不存在,就创建一个截断的(truncated)(空)文件。
      • O_TRUNC : 如果文件已存在,就截断它(长度被截为0,属性不变)
      • O_APPEND: 在每次写操作前,设置文件位置到文件的结尾
    • O_RDWR: 可读可写
  • mode指定新文件的访问权限位

进程调用close关闭一个打开的文件

#include <unistd.h>

int close(int fd);

                //返回: 若成功则为0,若出错则为-1

10.4 读写文件
#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

  • read:read函数从描述符fd的当前文件位置拷贝最多n个字节到存储器buf
  • write:write函数从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置
10.8 共享文件

内核有三个相关的数据结构来表示打开的文件:

  • 描述符表:
    每个进程都有它独立的描述符表。
    它的表项是由进程打开的文件描述符来索引的。
    每个打开的描述符表项指向文件表的一个表项。

  • 文件表:打开文件的集合是由一张文件表表示的。
    所有的进程共享这张表。
    每个文件表项的部分组成是
    1.当前的文件位置
    2.引用计数
    3.以及一个指向v-node表中对应表项的指针。

  • v-node表:
    所有的进程共享这张表。每个表项包含stat结构的大多数信息

共享情形1:
同一个filename调用open函数多次,每个描述符有其自己的文件位置,对不同描述符的读操作可以从不同位置获取数据。

#include "csapp.h"

int main(int argc, char *argv[])//argv[1]为abcde.txt(abcde)
{
    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);
    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;
}


结果:在这里插入图片描述
都是从文件的起始位置开始读。

共享情形2:
调用fork后,子进程有一个父进程描述符表副本。父子进程共享相同的打开文件表。共享相同的文件位置。

#include "csapp.h"

int main(int argc, char *argv[])
{
    int fd1;
    char c1, c2;
    char *fname = argv[1];
    fd1 = open(fname, O_RDONLY, 0);
    read(fd1, &c1, 1);
    if (fork() == 0) {
	read(fd1, &c2, 1);
	printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
    }
		wait(NULL);
	read(fd1, &c2, 1);
	printf("Child: c1 = %c, c2 = %c\n", c1, c2);
    exit(0);
    return 0;
}

结果:在这里插入图片描述
父子进程读写同一处文件位置,各自的读都相继进行。

10.9 I/O重定向

I/O重定向如何工作?

使用dup2函数

#include<unistd.h>

int dup2(int oldfd,int newfd);

            返回:若成功则为非负的描述符,若出错则为-1

dup2函数拷贝描述符表表项 oldfd 到描述符表表项 newfd ,覆盖newfd。
如果newfd已经打开,dup2会在拷贝oldfd之前关闭newfd。简单的说就是把newfd的文件位置指向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);
    read(fd1, &c1, 1);
	dup2(fd2, fd3);
    
    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;
}


在这里插入图片描述
fd2与fd3经过dup2()之后指向同一个文件位置,读也是相继的读。

实例:

#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;
}
//结果:pqrswxyznef

fd1因为O_TRUNC,截断之前所有的内容。
之后写入pqrs。此时fd1的光标在s后面。
然后fd3因为O_APPEND,在s后面写入jklmn,此时光标在n后面。
然后fd2=dup(fd1);fd2的文件位置指向fd1。
再从s后面的光标写入wxyz,结果就是把jklm给覆盖了。
最后在fd3之后写入ef。
最后结果就是:pqrswxyznef

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值