计算机系统基础学习技术日志三(I/O)

文件

Linux中有一句话叫,一切皆文件。每个Linux文件都有一个类型来表明它在系统中的角色:
1.普通文件:包含任意数据。应用程序常常要区分文本文件和二进制文件,文本文件即只含有ASCII或Unicode字符的普通文件,二进制文件是所有其他文件
2.目录文件:包含一组链接的文件。其中每个链接都将一个文件名系引(filename)映射到一个文件,这个文件可能是另一个目录。用来操作目录的命令主要有 mkdir, ls, rmdir。目录是以树状结构组织的,根目录是 /(slash)。内核会为每个进程保存当前工作目录(cwd, current working directory),可以用 cd 命令来进行更改。我们通过路径名来确定文件的位置,一般分为绝对路径和相对路径。
3.套接字:用来与另一个进程进行跨网络通信的文件。
4.链接文件。
5.命名管道。
6.字符文件。
7.块设备。
在这里插入图片描述

I/O

打开文件:一个应用程序通过要求内核打开相应的文件,来表明它想要访问一个I/O设备。这时内核会返回一个小的非负整数,叫做文件描述符(它是关键!它返回的是当前所能用的最小数):用来标识这个文件。
Linux shell 创建的每个进程开始时都有三个打开的文件:标准输入(文件描述符为0),标准输出(1),标准错误(2)

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

读写文件:一个读操作就是从文件复制n>0个字节到内存,从K位置开始,当超过时会触发一个称为EOF的条件。

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

关闭文件:当应用完成对文件的访问之后,就通知内核关闭这个文件。作为相应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。

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

共享文件
两个描述符引用两个不同的打开文件。 描述符1(stdout)指向终端,描述符4指向打开磁盘文件
在这里插入图片描述
两个不同的描述符通过两个不同的打开文件表条目共享同一磁盘文件
例如,使用相同的文件名参数调用两次open
在这里插入图片描述
子进程继承其父进程的打开文件
注意:exec函数保持不变的情况(使用fcntl进行更改)
在fork呼叫之前:
在这里插入图片描述
子进程继承其父进程的打开文件
后叉:
孩子的表与父母的表相同,并为每个引用+1
在这里插入图片描述

C语言标准库里的标准I/O函数

在此之外就是一个小知识点,是C语言标准库里的标准I/O函数,其实它们也就是对之前说的系统函数进行了封装,得以获得更好的可移植性:
打开和关闭文件: fopen, fclose
读取和写入字节: fread, fwrite
读取和写入行: fgets, fputs
格式化读取和写入: fscanf, fprintf

书上三道习题

以下题目都需通过gcc链接生成可执行文件运行
第一题:
假设文件内容为abcde

#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,fd2,fd3打开同一个文件,
但是dup2(fd2,fd3),使fd3指向fd2打开的文件,
所以fd1,fd2输出的是a,fd3输出的是3,
则输出结果为c1=a,c2=a,c3=b

第二题:

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

此处用到了fork的内容
当s=0时,父进程先执行,输出
Parent: c1 = a, c2 =b
Child: c1 = a, c2 = c

s=1,则子进程先执行,输出
Child: c1 = a, c2 = b
Parent: c1 = a, c2 =c

第三题:

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

首先在文件中写出的是pqrs毫无疑问,然后O_APPEND的作用就是在文本后面输入那就是pqrsjklmn,接下来很关键的一条语句是fd2 = dup(fd1),意思就是fd2指向了fd1的位置,然后输出,那就是在s后面输出wxyz,让我没想到的是wxyz覆盖了jklmn的位置,当然只覆盖了四个,留下了n,最后就是在末尾输出ef。
所以最后输出结果是:pqrswxyznef

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值