本篇涉及文件的创建、打开、读和关闭。
文件为操作系统服务和设备提供了一个简单而一致的 接口
。“接口”指的是一种约定或标准,通过提供一个一致的接口,可以为上层隐藏底层硬件和服务的复杂性,上层无需关注它们的具体实现细节。
比如操作系统中的文件系统。文件可以存储在硬盘上,也可以存储在光盘上、磁带上。不同的硬件之间差别很大,文件系统也不相同。但是只需要抽象出 5 个基本的函数:open
、close
、read
、write
和 ioctl
,应用程序就可以不用理会文件用的是什么文件系统、存储在哪里,就可以完成文件的操作。这 5 个基本函数的定义就是接口。
在 Linux 中,一切(或几乎一切)都是文件。
操作系统的核心部分,即内核,是一组 设备驱动程序
。它们是一组对系统硬件进行控制的底层 接口
。
为了向用户提供一个一致的接口,设备驱动程序封装了所有与硬件相关的特性。硬件的特有功能通常可通过 ioctl
(用于 I/O 控制) 系统调用来提供。
低层次文件访问
使用 系统调用
函数访问文件,属于低层次文件访问方式。
write 函数
#include <unistd.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char *argv[])
{char *test_str = "Here is some data\n";write(1, test_str, strlen(test_str));exit(0);
}
- 头文件
unistd.h
包含了许多与 POSIX 操作系统 API 相关的定义和函数原型,例如write
函数。 - 头文件
stdlib.h
包含exit
函数 - 头文件
string.h
包含处理字符串的函数原型,例如strlen
。 write
函数是系统调用,原型为size_t write (int __fd, const void *__buf, size_t __n)
,在本例中,将 test_str 中的内容写入到文件描述符 1 所代表的设备上(标准输入)。
write 函数返回写入的字节数,出错返回 -1。- 调用
exit
函数来结束程序。这里的参数0表示程序正常退出。
read 函数
#include <unistd.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char *argv[])
{char error_read_message[] = "Error writing to stdout\n";char buffer[8];int nread;nread = read(0, buffer, 8);if(nread == -1)write(2, error_read_message, strlen(error_read_message));write(1, buffer, nread);exit(0);
}
- 调用
read
函数从文件描述符 0(即标准输入)读取最多 8 个字节的数据到 buffer 中,并将读取的字节数存储在 nread 中。注意它返回实际读入的字节数,这可能会小于 8 。 - 读取最多 8 个字节数据,这表示多于 8 个字符时,只会取前 8 个字符,不会导致溢出。
- 在 shell 中运行这个程序,read 函数从标志输入中读取数据,shell 将标准输入映射到键盘上。当按下回车键时,shell 将输入的内容发送给程序。注意输入的数据包含换行符。