第10章-系统级I/O

UNIX I/O

所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,``而所有的输入和输出都被当做相应文件的读和写来执行,所以内核可以利用称为 Unix I/O 的简单接口来处理输入输出,比如使用 open() 和 close() 来打开和关闭文件,使用 read() 和 write() 来读写文件,或者利用 lseek() 来设定读取的偏移量等等。

Linux shell创建的每个进程开始时都有三个文件:

0:标准输入
1:标准输出
2:标准错误

因此用户所打开的第一个文件的描述符为3,并且以后的所有文件的描述符都是从3开始的从小到大的整数。

文件分类

每个Linux文件都有一个类型(type)来表明它在系统中的角色:

  • 普通文件(binary file):包含任意数据
  • 目录(directory):相关一组文件的索引(可以简单理解为Windows里的快捷方式,但不全是)
  • 套接字(socket):和另一台机器上的进程通信的类型

文件操作

文件操作有:open,close,read,write,stat,lseek,dup2

打开文件

进程通过调用open函数打开一个已存在的文件或创建一个新的文件。

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

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

返回:若成功则为新的文件描述符,若出错则为-1
参数分析
(1)char* filename:文件名,指向一个字符串的指针!
(2)int flags:指明进程打算如何访问该文件
(3)mode_t mode:指定了新文件的访问权限位

  • 对于flags有如下宏定义:
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:其他人(任何人)能够执行这个文件
关闭文件
#include <unistd.h>
int close(int fd);

返回:若成功返回则0,若出错则为-1。
参数:想关闭文件的文件描述符

注意:关闭一个已关闭的描述符会出错

读写文件

应用程序是通过分别调用read和write函数来执行输入和输出的

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

返回:成功则为读取到的字节数,出错则为-1,若EOF(读完)则为0。
参数分析
(1)int fd:打开文件的文件描述符
(2)void *buf:读出来的结果存放的位置
(3)size_t n:最多读取的字节个数
read函数的作用:从描述符为fd的文件的当前位置复制最多n个字节到位置buf。

一个程序使用read和write函数调用一次一个字节地从标准输入复制到标准输出,如下所示。

/* $begin cpstdin */
#include "csapp.h"

int main(void) 
{
    char c;

    while(Read(STDIN_FILENO, &c, 1) != 0) 
	Write(STDOUT_FILENO, &c, 1);
    exit(0);
}
读取文件元数据

元数据:元数据就是用来描述数据的数据,由内核维护,可以通过stat和fstat函数来访问。

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

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

stat函数以一个文件名为输入,并填写如下列所示的一个stat数据结构中的各个成员。fstat函数是相似的,只不过是以文件描述符而不是以文件名作为输入。

保存信息的stat数据结构:

struct stat
{
    dev_t           st_dev;     // Device
    ino_t           st_ino;     // inode
    mode_t          st_mode;    // Protection & file type
    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 type (if inode device)
    off_t           st_size;    // Total size, in bytes
    unsigned long   st_blksize; // Blocksize for filesystem I/O
    unsigned long   st_blocks;  // Number of 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 change
}
共享文件

Linux文件可以用很多方式进行共享,内核通常用三个相关的数据结构来表示打开的文件:

  • 描述符表: 每个进程都有独立的描述符表,表项是由进程打开的文件描述符来索引的。 每个描述符表项只想文件表中的一个表项。

  • 文件表:
    打开文件的集合是由一张文件表来表示的,所有进程共享。
    它记录了当前文件的位置,当前指向该表项的描述符表项数(成为引用计数)和一个指向v-node表中对应表项的指针。 当引用计数为0是,内核会自动删除这个文件表表项。

  • v-node表:
    所有进程共享,包含了stat结构中的大多数信息,包括st_mode和st_size成员。

示例:子进程继承父进程的打开文件

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

abcde.txt内容:abcde

运行结果

Parent: c1 = a, c2 = b
Child: c1 = a, c2 = c

过程分析
1.先打开了abcde.txt,返回文件描述符fd1,并读取文件里的第一个字符‘a’,光标停留在了ab之间;
2.进行了fork,fork创建的子进程复制了父进程的环境。
3.然后两个进程都遇到了sleep,接着先继续执行子进程,它读了一个字符,因为先前光标停留在ab之间,所以此时c2读到的是b
4.child执行完后回到父进程,父进程和子进程的文件表表项相同,所以光标在b后,往后读一个,读到了c
5.最后进行输出得到了如上所示的结果。

I/O重定向

I/O重定向能帮助用户将磁盘文件和标准输入输出联系起来

#include <unistd.h>
int dup2(int oldfd,int newfd);
int dup(int oldfd);
  • dup2函数:用oldfd的文件表表项替换掉newfd的文件表表项,此外如果newfd是打开的状态的话,会需要先关闭掉newfd。
  • dup函数:直接再建一个文件,文件的文件项就是oldfd

示例

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

abcde.txt内容:pqrswxyzef
运行结果

pqrswxyznef

过程分析
1.打开文件,返回fd1,写入“pqrs”,此时光标位于文件末尾
2.fd3打开这个文件,O_APPEND表明光标停留在文本文件的最后一个字符后面,并写入"jklmn"
3.dup函数,将文件描述符表fd1文件指向了fd2,简单理解就是fd2和fd1是一样的,所以可知fd2光标在pqrs后面
4.写fd2,此时写的"wxyz"会覆盖"jklm"
5.写fd3,fd3的光标在最末尾,将ef写在了最后

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
系统根据B/S,即所谓的电脑浏览器/网络服务器方式,运用Java技术性,挑选MySQL作为后台系统系统主要包含对客服聊天管理、字典表管理、公告信息管理、金融工具管理、金融工具收藏管理、金融工具银行卡管理、借款管理、理财产品管理、理财产品收藏管理、理财产品银行卡管理、理财银行卡信息管理、银行卡管理、存款管理、银行卡记录管理、取款管理、转账管理、用户管理、员工管理等功能模块。 文中重点介绍了银行管理的专业技术发展背景和发展状况,随后遵照软件传统式研发流程,最先挑选适用思维和语言软件开发平台,依据需求分析报告模块和设计数据库结构,再根据系统功能模块的设计制作系统功能模块图、流程表和E-R图。随后设计架构以及编写代码,并实现系统能模块。最终基本完成系统检测和功能测试。结果显示,该系统能够实现所需要的作用,工作状态没有明显缺陷。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。进入银行卡列表,管理员可以进行查看列表、模糊搜索以及相关维护等操作。用户进入系统可以查看公告和模糊搜索公告信息、也可以进行公告维护操作。理财产品管理页面,管理员可以进行查看列表、模糊搜索以及相关维护等操作。产品类型管理页面,此页面提供给管理员的功能有:新增产品类型,修改产品类型,删除产品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值