Unix的系统IO

本文为博主转载文章
原文链接: https://blog.csdn.net/u013075553/article/details/103395816

Unix 系统IO

文件

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

  • 普通文件(regular file)包含任意数据
  • 目录(direction)相关一组文件的索引
  • 套接字(socket)和另一台机器上的进程通信的类型

这篇博客主要介绍普通文件:

  • 文本文件:
    文本文件是只包含ASCIIUnicode字符的普通文件,就是一系列的文本行,每行以一个新行字符\n结尾,其数字值是0xa,和ASCII码中的line feed字符(LF)一样。在Windows网络协议之中是\r\n(0xd 0xa)以结尾。
  • 二进制文件
    二进制文件则是除文本文件外的普通文件,如我们的程序、图片、视频等

打开文件

当我们想要读或写一个文件的时候,我们首先需要告诉系统内核,我们要访问到这个文件。内核(open函数)则会返回一个小的非负整数(描述符),我们后续的操作都得用到这个描述符。
在程序的一开始,已经默认打开了三个文件:

  • 0: standard input(stdin)
  • 1: standard output(stdout)
  • 2: standar error(stderr)
#include<sys/types.h>
#incldue<sys/stat.h>
#include<fcntl.h>

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

  • 1
  • 2
  • 3
  • 4
  • 5

参数介绍:

  • filename
    要打开或创建的目标文件的名字
  • flags
    打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行运算,构成falgs
  • O_RDONLY:  只读打开
  • O_WRONLY:  只写打开
  • O_RDWR:    可读可写
    上面的这三个常量,必须制定一个且只能指定一个
  • O_CREAT:  若文件不存在,则创建它,需要使用mode选项。来指明新文件的访问权限
  • O_APPEND:  追加写,如果文件已经有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容
  • O_TRUNC:  如果文件存在,则截断它(删除已有的内容,重新写入)
  • mode
    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);

  • 1
  • 2
  • 3
  • fd:要关闭的文件的描述符。
  • 返回值:若成功返回0
  • 出错则返回-1

注:当一个进程终止时,内核会自动关闭它所有打开的文件

读取文件

#include <unistd.h>

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

  • 1
  • 2
  • 3

参数:

  • fd:要读取的文件的描述符。
  • buf:读取到的数据要放入的缓冲区。
  • count:要读取的字节数。

返回值:

  • 若成功返回读到的字节数,若已到文件结尾则返回0
  • 若出错则返回-1并设置变量errno的值。

注:这里的size_t是无符号整型,ssize_t是有符号整型。

写入文件

#include <unistd.h>

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

  • 1
  • 2
  • 3

参数:

  • fd:文件描述符;
  • buf:指定的缓冲区,即指针,指向一段内存单元;
  • n:要写入文件指定的字节数;

返回值:

  • 写入文档的字节数(成功);-1(出错)

write函数把buf中的n个字节写入描述符fd所指的文件,成功时返回写的字节数,错误时返回-1.

例子

#include "csapp.h"

int main(void)
{
char c;
int fd = open(“abc.txt”,O_WRONLY|O_CREAT|O_TRUNC);
while(read(STDIN_FILENO, &c, 1) != 0)
write(fd, &c, 1);
close(fd);
exit(0);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这是一个比较简单的例子,把输入进终端的内容储存到文件abc.txt里面。
下面是运行的结果:

hpj@黄培纪:/mnt/d/code/C/Computer Systems/chap10_code$ ./test
test text
^C

 
  
  
  • 1
  • 2
  • 3

abc.txt里面的内容为:

test text

注:上面还有一个换行符(\n)没显示出来

  • 1
  • 2
  • 3

重定向

在内核里,有三个相关的数据结构用于表示所打开的文件:

  • 描述符表
    每个进程都有自己的描述符表(Descriptor table),表项是由文件的描述符来索引
  • 文件表
    打开了的文件的集合,所有的进程都共享这一张表。表项包括文件的位置、引用的计数(reference count)(当前指向该表项的描述符数量)。直到这个数字变为了0,系统内核才会将其删除,否则不会。
  • v-node
    所有的进程共享这张表,且每个表项包含stat结构的大多数信息。


下面看一个代码:

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

<span class="token function">Read</span><span class="token punctuation">(</span>fd1<span class="token punctuation">,</span> <span class="token operator">&amp;</span>c1<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Read</span><span class="token punctuation">(</span>fd2<span class="token punctuation">,</span> <span class="token operator">&amp;</span>c2<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Read</span><span class="token punctuation">(</span>fd3<span class="token punctuation">,</span> <span class="token operator">&amp;</span>c3<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"c1 = %c, c2 = %c, c3 = %c\n"</span><span class="token punctuation">,</span> c1<span class="token punctuation">,</span> c2<span class="token punctuation">,</span> c3<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">Close</span><span class="token punctuation">(</span>fd1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Close</span><span class="token punctuation">(</span>fd2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Close</span><span class="token punctuation">(</span>fd3<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

读取下面的这个文件abcde.txt

abcde

 
  
  
  • 1

运行结果入下:

hpj@黄培纪:/mnt/d/code/C/Computer Systems/chap10_code$ ./ffiles1 abcde.txt
c1 = a, c2 = a, c3 = b

 
  
  
  • 1
  • 2

这是因为这里用到了一个dup2函数,改变了fd3描述符指向的文件,这也就是重定向的过程,如下图所示:

再来看一个例子:

#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 <span class="token operator">=</span> <span class="token function">Open</span><span class="token punctuation">(</span>fname<span class="token punctuation">,</span> O_APPEND<span class="token operator">|</span>O_WRONLY<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Write</span><span class="token punctuation">(</span>fd3<span class="token punctuation">,</span> <span class="token string">"jklmn"</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
fd2 <span class="token operator">=</span> <span class="token function">dup</span><span class="token punctuation">(</span>fd1<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/* Allocates new descriptor */</span>
<span class="token function">Write</span><span class="token punctuation">(</span>fd2<span class="token punctuation">,</span> <span class="token string">"wxyz"</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Write</span><span class="token punctuation">(</span>fd3<span class="token punctuation">,</span> <span class="token string">"ef"</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">Close</span><span class="token punctuation">(</span>fd1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Close</span><span class="token punctuation">(</span>fd2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Close</span><span class="token punctuation">(</span>fd3<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

运行结果入下:

hpj@黄培纪:/mnt/d/code/C/Computer Systems/chap10_code$ ./ffiles3 abc.txt

 
  
  
  • 1

abc.txt文件的内容入下:

pqrswxyznef

 
  
  
  • 1

大致过程入下:
首先Write(fd1, "pqrs", 4);pqrs写入到abc.txt之中;
然后Write(fd3, "jklmn", 5);jklmn写入到abc.txt之中;
然后在原本fd1的指向之下,Write(fd2, "wxyz", 4);wxyz写入到pqrs的后面,把jklmnjklm覆盖了,变成了wxyzn
最后Write(fd3, "ef", 2);wxyzn后继续写入ef
所以abc.txt的内容就是pqrswxyznef

最后再来看一个例子:

#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()) {
int status;
wait(&status);
Read(fd1, &c2, 1);
printf(“Parent: c1 = %c, c2 = %c\n”, c1, c2);
} else {
/* Child */
Read(fd1, &c2, 1);
printf(“Child: c1 = %c, c2 = %c\n”, c1, c2);
}
return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

读取abcde.txt的结果如下:

hpj@黄培纪:/mnt/d/code/C/Computer Systems/chap10_code$ ./ffiles2 abcde.txt
Child: c1 = a, c2 = b
Parent: c1 = a, c2 = c

 
  
  
  • 1
  • 2
  • 3


子进程把父进程的描述符表也复制了一份,但指向的仍然还是同一个文件表项,所以执行Read(fd1, &c2, 1);的结果不一样。


参考资料

                                </div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
                </div>
本文为博主转载文章
原文链接: https://blog.csdn.net/u013075553/article/details/103395816

Unix 系统IO

文件

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

  • 普通文件(regular file)包含任意数据
  • 目录(direction)相关一组文件的索引
  • 套接字(socket)和另一台机器上的进程通信的类型

这篇博客主要介绍普通文件:

  • 文本文件:
    文本文件是只包含ASCIIUnicode字符的普通文件,就是一系列的文本行,每行以一个新行字符\n结尾,其数字值是0xa,和ASCII码中的line feed字符(LF)一样。在Windows网络协议之中是\r\n(0xd 0xa)以结尾。
  • 二进制文件
    二进制文件则是除文本文件外的普通文件,如我们的程序、图片、视频等

打开文件

当我们想要读或写一个文件的时候,我们首先需要告诉系统内核,我们要访问到这个文件。内核(open函数)则会返回一个小的非负整数(描述符),我们后续的操作都得用到这个描述符。
在程序的一开始,已经默认打开了三个文件:

  • 0: standard input(stdin)
  • 1: standard output(stdout)
  • 2: standar error(stderr)
#include<sys/types.h>
#incldue<sys/stat.h>
#include<fcntl.h>

猜你喜欢

转载自blog.csdn.net/xiongdan626/article/details/103426127