深入理解计算机系统基础系列09

前言

never give up!
无论如何,never give up!

正文

系统级 I/O

Linux shell下面创建的每个进程都有三个打开的文件
0:标准输入
1:标准输出
2:标准错误
文件的操作主要有:open,read,write,stat,dup2,close,lseek;
每个Linux文件都有一个类型:普通文件,目录,套接字等
(注套接字是网络编程上的东西,暑假我用socket写过一个服务端和客户端简单交流的CPP文件,之后会献上)

open函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

open函数会返回一个文件描述符,由于Linux下面一切皆文件

flag参数

O_RDONLY:只读
O_WRONLY: 只写
O_RDWR: 可读可写
O_CREAT: 文件不存在,就创建一个它的截断的空文件。(就是创建一个新空白文件)
O_TRUNC: 如果文件已经存在,就截断它。(清空它)
O_APPEND: 在每次写操作前,设置文件位置到文件结尾处。

mode参数

S_IRUSR:使用者(拥有者)能够读这个文件
S_IWUSR:使用者(拥有者)能够写这个文件
S_IXUSR:使用者(拥有者)能够执行这个文件
S_IRGRP:拥有者所在组的成员能够读这个文件
S_IWGRP:拥有者所在组的成员能够写这个文件
S_IXGRP:拥有者所在组的成员能够执行这个文件
S_IROTH:其他人(任何人)能够读这个文件
S_IWOTH:其他人(任何人)能够写这个文件
S_IXOTH:其他人(任何人)能够执行这个文件
这里的mode用一个三位的八进制数进行标识,也就意味着一共九位01二进制来表示是否可读,可写,可执行。

关闭文件

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

读写文件

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

read函数从描述符为fd的文件的当前位置复制最多n个字节到位置buf。三种返回值:-1出错,0表示EOF,或者是读取到的字节的个数。
write函数从内存buf出至多复制n个字节到描述符为fd的当前文件位置。

读取元数据

调用stat()函数或者fstat()函数
作用为获取文件信息
返回值,成功返回1,失败返回-1

#include <unistd.h>
#include <sys/stat.h>

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

stat函数以一个文件名作输入,可以查看到文件的各种数据

struct stat
{
    dev_t     st_dev;     /* ID of device containing file */文件使用的设备号
    ino_t     st_ino;     /* inode number */    索引节点号 
    mode_t    st_mode;    /* protection */  文件对应的模式,文件,目录等
    nlink_t   st_nlink;   /* number of hard links */    文件的硬连接数  
    uid_t     st_uid;     /* user ID of owner */    所有者用户识别号
    gid_t     st_gid;     /* group ID of owner */   组识别号  
    dev_t     st_rdev;    /* device ID (if special file) */ 设备文件的设备号
    off_t     st_size;    /* total size, in bytes */ 以字节为单位的文件容量   
    blksize_t st_blksize; /* blocksize for file system I/O */ 包含该文件的磁盘块的大小   
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */ 该文件所占的磁盘块  
    time_t    st_atime;   /* time of last access */ 最后一次访问该文件的时间   
    time_t    st_mtime;   /* time of last modification */ /最后一次修改该文件的时间   
    time_t    st_ctime;   /* time of last status change */ 最后一次改变该文件状态的时间   
};

下面的这个代码

/* $begin statcheck */
#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);
}

该程序用来查看文件类型,和访问权限
S_ISREG(m)是否为普通文件
S_ISDIR(m)是否为目录文件
S_ISSOCK(m)是否为网络套接字

共享文件在这里插入图片描述

1.每个进程对应一张打开文件描述符表,这是进程级数据结构,也就是每一个进程都各自有这样一个数据结构;
2.内核维持一张打开文件表,文件表由多个文件表项组成,这是系统级数据结构,也就是说这样的数据结构是针对于整个内核而言的,每个进程都可共享的;

3.每个打开的文件对应一个i节点(i-node)数据结构(Linux下只有i节点没有v节点),由于这是每一个打开的文件与之对应的,因此这也是一个系统级数据结构,存在于内核中,非进程所独有。

(1)每一个打开文件实际上就是用一个file结构体进行描述的
(2)内核为所有的打开文件维持一张文件表,每个文件表项包含:
a.文件状态标志(读、写、同步、阻塞等)
b.当前文件偏移量
c.指向该文件的v节点表项的指针
在这里插入图片描述
总结
在这里插入图片描述

I/O重定向

例子:
ls>1.txt
shell加载执行ls程序,将标准输出重定向到1.txt
而后是dup2()函数

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

得到c1=a,c2=a,c3=b

#include <unistd.h>
int dup2(int oldfd,int newfd);

dup2函数做的其实就是用oldfd的文件表表项替换掉newfd的文件表表项,此外如果newfd是打开的状态的话,会需要先关闭掉newfd。

dup()函数

int dup(int oldfd);

dup()函数
新建一个文件项
题目:

#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
*/

先写出pqrs,fd3追加模式,写成jklmn五个字符,所以现在是pqrsjklmn,dup得到新的,光标停在pqrs后,
然后fd2,wxyz覆盖jklm,最后得到ef
总结为pqrswxyznef

后言

rethink,rethink,rethink

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值