- 函数 read
- 函数 write
- 完整代码
- 小结
本着在使用中学习的原则,现在提出一个需求,只使用 linux 系统接口来实现文件拷贝命令。在上一篇博文中,已经简单了解了库函数和系统接口的区别。本文将继续学习 write
,另外还将学习另外两个系统接口 read
和 open
.
代码
为了方便学习,我把所有的错误处理的代码删除了。如果你想看完整代码,请跳到倒数第二节完整代码那里去。
- 代码
- 编译
编写完成后,使用正的命令编译后生成一个 mycp 的可执行文件。
- 执行
使用下面的方法来执行 mycp。意思是复制文件 mycp.c 到新文件 test 中去。
看完后,不要慌,待我解释,一下子学习 3 个函数,可能让你难以接受。
函数 open
函数 open 的原型如下。
在前面的代码中,用到了两次 open 函数,而且函数的参数个数也不一样,其实它和 printf 一样,也是一个可变参数函数。
这行的意思是打开文件 mycp.c
(这个值是通过 argv[1]
传过来的),后面的 O_RDONLY 的意思是,打开这个文件后,你只能读取。返回值 srcfd 是一个整数,这个整数在本进程内唯一的标识了 mycp.c 这个文件。它有个专用的名字,叫文件描述符,有关这个概念以后会慢慢深入,这里你就认为它表示 mycp.c 这个文件。
这一行的 open
传了 3 个参数,这个函数是创建一个文件 test
(这个值是通过 argv[2]
传过来的),第二个参数是 O_CREAT | O_WRONLY
,表示创建一个文件 test
, 这个文件是只写的。0666
是8进制数 666,表示 test
文件的权限是 -rw-rw-rw-
。返回值 dstfd
唯一了标识了 test
这个文件。
open 函数的用法
-
flags
有三个必选项,分别是O_RDONLY, O_WRONLY, O_RDWR
. 这三个值是互斥的,只能选一个。(如果你看过APUE这本书,那上面提到的是5选一,但是这里我不想讲另外的两个) -
flags
有很多可选项,O_CREAT, O_APPEND, O_EXCL, O_TRUNC, O_NONBLOCK
. 这几个值是可以多选的。当然不能和必选项发生冲突。 - 如果填写了
O_CREAT
这个可选项,那个 open 函数的第 3 个参数mode
就必需写。O_EXCL
只和O_CREAT
配合使用。如果O_EXCL
开启了,而且要创建的文件如果已存在,函数返回 -1。 -
O_APPEND
表示已追加的方式打开文件。 -
O_TRUNC
表示打开文件后,将长度截断成0. -
O_NONBLOCK
针对设备文件,比如屏幕,网络,表示以非阻塞的方式打开 IO. 这个概念现在不提。
除了以上的选项外,linux 还提供了其它的选项,这里就不说了,因为不太常用。上面的选项目前对我们来说,已经足够。随着学习的深入,遇到了再讨论也不迟。如果想了解更多选项,请向 man
寻求帮助。在终端中输入man 2 open
,就可以看到 open
的详细介绍。
man 2 open
中的 2,表示在 man 手册中的第2章查看 open,第 2 章描述的全部是 linux 系统接口。而第 3 章描述的都是C语言库函数。
flag 选项示例
函数 read
- 函数原型
size_t你可以理解成 unsigned int,而 ssize_t 你可以理解成 int。
关是读上面的mycp
代码你可能就会使用 read
函数了,它表示从文件 fd
中期望读取 count
字节的数据。
它的返回值表示实际读取到的字节数。如果返回值为 0,表示已经读取到了文件末尾,如果为 -1,表示读取出错。
题外话:如果你是从设备(比如标准输入或网络中)读取数据,read 默认情况下阻塞,它会一直等待有数据到来为止才返回。如果一直没有数据到来,那么本进程会被操作系统投入睡眠。
函数 write
write 函数也很容易理解,表示我期望写入 count 个字节的数据。返回值表示实际写入的数据。
对于普通文件来说,返回值必须和 count 相等,否则意味着出错。
对于设备来说,返回值可以小于 count 的值,比如向网络中写数据。
题外话:如果你是向设备(比如标准输入或网络中)写数据,write 默认情况下阻塞,除非缓冲区有空位。如果缓冲区一直是满的,那么本进程会被操作系统投入睡眠。
完整代码
小结
前面简述了 open read write
函数。但是并没有讨论过多的细节。实际上,在读写文件的时候,还有一个偏移量(offset)的概念。
比如我有这样的需求,我想从文件的第 5个字节开始读数据,或者向文件的第 10 个字节开始写数据,要怎么办?这个问题将在后面的文章中继续讨论。