20155335 俞昆《信息安全系统设计基础》第十三周

一、Unix I/O 的定义

将设备映射为文件的方式,允许UNIX内核引出一个简单、低级的应用接口,称为UNIX I/O.

输入是从I/O设备拷贝数据到主存,输出是从主存拷贝数据到I/O设备。

一个文件就是一个字节序列.

所有的I/O设备,如网络、磁盘、和终端,都被模型化为文件,而所有的输入和输出都被当做想对应的文件的读写来执行。

1、打开文件: 一个应用程序通过要求内核来打开文件,内核返回一个小的非负整数(描述符),内核记录有关这个文件的所有的信息,应用程序只需要记住这个描述符。

#include <sys/types.h>

#include <sys/stat.h>

<br>#include<fcntl.h>

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

//Returns: new file descriptor if OK, −1 on error

其中打开标志有三个flags参数,即

.  O_RDONLY: Reading only

  • .  O_WRONLY: Writing only

  • .  O_RDWR: Reading and writing

2.关闭文件

   #include<unsitd.h>

   int close(int fd)

3.读文件

在系统I/O中读写文件用的系统函数为read()和write()函数来执行。

            #include <unistd.h>  

            ssize_t read(int fd,void * buf,size_t n);  

             ssize_t write(int fd,void *buf,size_t n);  

read函数从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf。返回值-1表示一个错误,而返回值0表示EOF。否则,返回值表示的是实际传送的字节数量。而write函数从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置。返回值要么为-1要么为写入的字节数目。

#include "csapp.h"  
  
int main(void)   
{  
    char c;  
  
    while(Read(STDIN_FILENO, &c, 1) != 0)   
    Write(STDOUT_FILENO, &c, 1);  
    exit(0);  
} 
size t 与ssize t的区别
已知,size t和ssize t是在标准函数库中定义的。这个类型足以用来表示对象的大小。
size t的真实类型与操作系统有关,在32位架构中被普遍定义为:
typedef unsigned int size_t
size_t 在32位架构上是4字节,在64位架构上是8字节,在不同架构上进行编译时需要注意几个问题。
而int在不同架构下都是4字节,与size_t不同,int为带符号数,size_t为无符号数。
有些情况下,read和write的传送的字节比应用程序要求的少,出现这种情况的原因是:
读时遇到EOF,此时read返回0来发出EOF信号。
从终端读入文本行,如果打开文件与终端相关联,那么每个read函数将以此传送一个文本行,返回的不足值等于文本行的大小
读和写网络套接字,可能会出现阻塞现象。
实际上,除了EOF,在读取磁盘文件时,将不会遇到不足时,而是写在磁盘文件时,也不会遇到不足值。然而,如果想创建健壮的网络应用,必须反复调用read和write处理不足值,直到所需要的字节传送玩毕。
四、用RIO包健壮地读写
这个包会处理上面的不足,RIO提供了方便,健壮和高效的I/O,提供了两类不同的函数。
无缓冲的输入和输出函数直接在存储器和文件之间传送数据,在没有应用级缓冲,他们对将二进制数据读写到网络和从网络读写二进制数据尤其有用。
带缓冲的输入函数如下:
ssize_t rio_readn(int fd,void *usrbuf,size_t n);   
ssize_t rio_writen(int fd,void *usrbuf,size_t n); 
对同一个描述符,可以任意交错的调用rio_read和rio_writen.一个文本行的末尾都有一个换行符,读取文本的行数怎么办,使用read读取换行符这个方法不是很妥当,可以调用一个包装函数(rio_readlieb),

五.读取文件元数据
应用程序能够通过调用stat和fstat函数股检索到关于文件的信息(有时称为文件的元数据)
#include <sys/stat.h>  
#include <unistd.h>  
int stat(const char *filename,struct stat *buf);  
int fstat(int fd,struct stat *buf);
若成功,返回0,若出错则为-1,stat以一个文件名为输入,并且填充buf结构体。fstat函数只不过是以文件描述符而不是文件名作为输入。
struct stat {  
#if defined(__ARMEB__)  
    unsigned short st_dev;  
    unsigned short __pad1;  
#else  
    unsigned long  st_dev;  
#endif  
    unsigned long  st_ino;  
    unsigned short st_mode;  
    unsigned short st_nlink;  
    unsigned short st_uid;  
    unsigned short st_gid;  
#if defined(__ARMEB__)  
    unsigned short st_rdev;  
    unsigned short __pad2;  
#else  
    unsigned long  st_rdev;  
#endif  
    unsigned long  st_size;  
    unsigned long  st_blksize;  
    unsigned long  st_blocks;  
    unsigned long  st_atime;  
    unsigned long  st_atime_nsec;  
    unsigned long  st_mtime;  
    unsigned long  st_mtime_nsec;  
    unsigned long  st_ctime;  
    unsigned long  st_ctime_nsec;  
    unsigned long  __unused4;  
    unsigned long  __unused5;  
};  
其中st_size成员包含了文件的字节大小。st_mode为文件访问许可位。
UNIX提供的宏指令根据st_mode成员来确定文件的类型:S_ISREG(),这是一个普通文件么;S_ISDIR(),这是一个目录文件么;
S_ISSOCK()这是一个网络套接字么。使用一下这个函数
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/stat.h>  
#include <sys/types.h>  
#include <unistd.h>  
int main()  
{  
    int fd,size;  
    struct stat buf_stat;  
    memset(&buf_stat,0x00,sizeof(buf_stat));  
    fd=stat("stat.c",&buf_stat);  
        printf("%d
",(int)buf_stat.st_size);  
    return 0;  
    }  
六.共享文件

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

  • 描述符表(descriptor table)每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表项指向文件表中的一个表项。
  • 文件表(file table)  打开文件的描述符表项指向问价表中的一个表项。所有的进程共享这张表。每个文件表的表项组成包括由当前的文件位置、引用计数(既当前指向该表项的描述符表项数),以及一个指向v-node表中对应表项的指针。关闭一个描述符会减少相应的文件表表项中的应用计数。内核不会删除这个文件表表项,直到它的引用计数为零。
  • v-node表(v-node table)同文件表一样,所有的进程共享这张v-node表,每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员。
  • 描述符1和4通过不同的打开文件表表项来引用两个不同的文件。这是典型的情况,没有共享文件,并且每个描述符对应一个不同的文件。

        多个描述符也可以通过不同的文件表表项来应用同一个文件。如果同一个文件被open两次,就会发生上面的情况。关键思想是每个描述符都有它自己的文件位置,所以对不同描述符的读操作可以从文件的不同位置获取数据。

        父子进程也是可以共享文件的,在调用fork()之前,父进程如第一张图,然后调用fork()之后,子进程有一个父进程描述符表的副本。父子进程共享相同的打开文件表集合,因此共享相同的文件位置。一个很重要的结果就是,在内核删除相应文件表表项之前,父子进程必须都关闭了他们的描述符。   

七.标准I/O

        标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性。标准I/O库处理很多细节。例如缓存分配,以优化长度执行I/O等。标准的I/O提供了三种类型的缓存。

 

(1)全缓存:当填满标准I/O缓存后才进行实际的I/O操作。  (2)行缓存:当输入或输出中遇到新行符时,标准I/O库执行I/O操作。  (3)不带缓存:stderr就是了。

八.重定向

  • I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来。unix> ls > foo.txt
  • I/O重定向是依靠dup2函数工作的。dup2函数拷贝描述符表表项oldfd到描述符表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开,dup2会在拷贝oldfd之前关闭newfd。

 

 

 




 







 

4.

原文地址:https://www.cnblogs.com/xieboke/p/8053356.html