系统级I/O

本文介绍了Linux系统级I/O的概念,包括标准I/O和系统I/O的区别,以及输入输出操作的过程。文章通过多个实验详细解释了文件描述符、文件位置指针、文件类型和权限等概念,帮助读者深入理解Linux系统I/O的工作原理。
摘要由CSDN通过智能技术生成

标准I/O和系统I/O

在这里插入图片描述
这是如今的I/O图示:我们现在大部分都在使用系统I/O以及标准I/O,当然不同情况下需求也不同!

  • 系统I/O是最低层的,最接触硬件的,因为是操作系统提供的
  • 标准I/O则是因为不同操作系统的系统调用不一样而应运而生,C说:我可以搞定~ 因此C中的一系列调用都是可以跨操作系统的,因此被称为标准I/O

系统级I/O

  • 输入输出(I/O)是在主存和外部设备(如磁盘驱动器,终端和网络)之间复制数据的过程。

一切皆文件:在Linux系统中经常用的一句话,在这将会得到深入理解。
所有的I/O设备都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备映射为文件的方式,允许Linux内核引出一个简单的应用接口:Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行:

  • 打开文件:一个应用程序通过要求内核打开相应的文件,来表明它想要访问一个I/O设备。这时内核会返回一个小的非负整数,叫做文件描述符(它是关键!它返回的是当前所能用的最小数):用来标识这个文件。
  • Linux shell 创建的每个进程开始时都有三个打开的文件:标准输入(文件描述符为0),标准输出(1),标准错误(2)
  • 改变当前文件的位置:对于每个打开的文件,内核保持着一个文件位置K,初始为0,应用程序能通过执行seek操作,显示的设置K
  • 读写文件:一个读操作就是从文件复制n>0个字节到内存,从K位置开始,当超过时会触发一个称为EOF的条件。
  • 关闭文件:当应用完成对文件的访问之后,就通知内核关闭这个文件。作为相应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中

文件

每个Linux文件都有一个类型来表明它在系统中的角色,主要有:

  • 普通文件(regular file):包含任意数据,如文本文件和二进制文件(但这哥俩在内核眼中没有区别,都是0101组成的)
  • 目录(directory):是包含一组Link的文件,每个Link都将一个文件名映射到一个文件。
    Linux内核将所有文件都组织成一个目录层次结构,由根目录:/ 确定。
    在这里插入图片描述
    所有用户对应主目录都在home下!如:~: /home/cy

接下来就通过一些代码实验来了解一下系统I/O叭~

实验一

#include "csapp.h"

int main(void) 
{
    char c;

    while(Read(STDIN_FILENO, &c, 1) != 0) 
	Write(STDOUT_FILENO, &c, 1);
    exit(0);
}

我们先来看看read的原型:
ssize_t read(int fd, void *buf, size_t n):

  • fd :文件描述符,你到底读哪个文件
  • buf: 缓冲区,从文件中读到内存位置buf
  • n:读的字节数

成功则返回实际传送的字节数量,失败则返回-1,返回0则表示EOF,即文件读完。

了解完函数就可以看出,这个就是一个死循环,STDIN_FILENO就是标准输入设备,也就是你的键盘啦,每输入一个字符,就显示一个字符到显示屏上
在这里插入图片描述
这里Ctrl+C 强制退出即可。

实验二

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

假设打开文件的内容为abcde

这个程序打开的是同一文件,分别用不同的文件描述符3,4,5来描述,接下来出现了dup2函数,这个函数其实就是用fd2覆盖fd3,其实就是现在打开fd3其实指向的是fd2喔。看下图,一开始如果读fd1,fd2,fd3是这样的
在这里插入图片描述
用了dup2之后,则变成这样
在这里插入图片描述
最重要的是光标会停在上回打开时的位置!!
接下来我们继续看代码,Read读fd1,读出一个字符a,读fd2,因为是重新打开,所以读出的还是a,之后读fd3,因为现在指向fd2,且刚才fd2已经读出一个字符,现在光标的位置在后面,因此,此次读出的字符为b,所以输出的是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结合起来了,首先定义了一个秒数,用pid号与1,则结果为0或1,之后先打开fd1,读出了一个字符a,之后fork,有两种可能

  • s=0:则父进程先执行,子进程睡1s,于是输出
    Parent: c1 = a, c2 =b
    Child: c1 = a, c2 = c
  • s=1,则子进程先执行,父进程睡1s,于是输出
    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;
}

这个是关于写文件的,打开fd1,向里面写入pqrs,打开fd3,向里面写入jklmn,之后用fd1覆盖fd2,再写其实是向fd1写,但因为此时fd1中光标在s后面,所以新写入的把后面的jklm覆盖掉,之后再打开fd3,此时光标在n后面,写入ef,因此最后的文件内容为pqrswxyznef
在这里插入图片描述
在这里插入图片描述

实验五

#include "csapp.h"

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

这个是判断文件类型的一个实验,如果是普通文件,类型就为regular,如果是目录文件,类型就为directory,其他则为other,在看用户读的权限,可读为yes,不可读为no,之后输出即可。
在这里插入图片描述
好啦,实验就到这里啦,撒花~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值