使用系统调用来处理文件
2015-01-26 17:55
155 查看
以下文字翻译自Beginning Linux Programming的小节Low-Level
File Access
每一个运转的程序——也叫进程——都会拥有一些相关联的文件描述符(file descriptor)。这是些不大的整数,可以用来使用打开了的文件或者设备。有多少个文件描述符可以使用,取决于系统如何配置。当一个程序开始运行时,通常有3个文件描述符已经打开。它们是:
0:标准输入
1:标准输出
2:标准错误流
你可以通过使用系统调用open,关联其他文件指示符到文件以及设备。这些已经被自动打开的文件指示符,允许你用write来创建程序。
write
系统调用write将把指针buf(write方法的参数)所指向的内存中的前nbyte(write方法的参数)个字节写入到文件描述符fildes(write方法的参数)所关联的文件中。返回的值为实际写入的字节数。这个值可能会小于nbyte,如果文件描述符有错,或者底层设备对数据块大小敏感。如果返回0,表示没有写入,如果返回-1,表示有错,错误的信息将从全局变量errno获得。
以下为语法:
#include <unistd.h>
size_t write(int fildes, const void *buf, size_t nbytes);
可以写一个程序, simple_write.c来试试:
#include <unistd.h>
#include <stdlib.h>
int main()
{
if ((write(1, “Here is some data\n”, 18)) != 18)
write(2, “A write error has occurred on file descriptor 1\n”,46);
exit(0);
}
这个程序简单地print一句信息到标准的输出流中。当程序退出时,所有的打开的文件描述符将被自动关闭,所以不需要显式地关闭。然而,当你使用的是缓冲的输出流时,根本就不用考虑这个。
$ ./simple_write
Here is some data
$
值得再次注意的是,write可能返回的字节数小于你指定字节数。这并不一定是错误。在你的程序中,你需要检查errno,然后写入剩余的数据。
read
read方法将从文件描述符fildes关联的文件中,读取nbytes个字节的数据,并放入到指针buf所指向的内存中。返回的值为所读取的真实的字节数,可能少于nbytes。如果返回0,表示没有读到任何数据,已经到达文件的末尾了。如果出错,这返回-1.
[align=left]#include <unistd.h>[/align]
[align=left]size_t read(int fildes, void *buf, size_t nbytes);[/align]
以下程序,simgle_read.c,将128个字节标准输入复制到标准输出中。如果输入的字节少于128,则将全部复制到输出流中。
[align=left]#include <unistd.h>[/align]
[align=left]#include <stdlib.h>[/align]
[align=left]int main()[/align]
[align=left]{[/align]
[align=left]char buffer[128];[/align]
[align=left]int nread;[/align]
[align=left]nread = read(0, buffer, 128);[/align]
[align=left]if (nread == -1)[/align]
[align=left]write(2, “A read error has occurred\n”, 26);[/align]
[align=left]if ((write(1,buffer,nread)) != nread)[/align]
[align=left]write(2, “A write error has occurred\n”,27);[/align]
[align=left]exit(0);[/align]
[align=left]}[/align]
如果执行这个文件,将看到如下输出:
[align=left]$ echo hello there | ./simple_read[/align]
[align=left]hello there[/align]
[align=left]$ ./simple_read < draft1.txt[/align]
[align=left]Files[/align]
[align=left]In this chapter we will be looking at files and directories and how to manipulate[/align]
[align=left]them. We will learn how to create files,$[/align]
在第一次执行中,你通过echo输入了内容到输入流中,然后通过管道符,传到了你的文件中。在第二次执行中,你从一个文件中导入了数据到输入流中,你可以看到输出的只是文件中的前部分内容。
可以注意到shell命令符紧跟在输出文字的最后一位,在这个文件中,128个字符没有产生所有的内容。
open
要产生新的文件描述符,需要使用open方法。
[align=left]#include <fcntl.h>[/align]
[align=left]#include <sys/types.h>[/align]
[align=left]#include <sys/stat.h>[/align]
[align=left]int open(const char *path, int oflags);[/align]
[align=left]int open(const char *path, int oflags, mode_t mode);[/align]
严格上讲,在POSIX标准下,你不需要include sys/types.h、sys/stat.h文件来使用open方法。当在某些UNIX系统下还是需要的。
简单地说,open创建了使用文件或者设备的途径。如果成功,返回文件描述符,然后可以用于read, write, 以及其他系统调用。文件描述符是独一无二的,并且不会与其他进程共享同一个描述符。如果两个程序同时打开了同一个文件,他们使用的是不一样的文件描述符。如果他们都写入数据到文件,则他们会继续写入到之前离开的位置。写入的数据不会交错存取,而是会覆盖掉另一个的数据。两个程序会各自维护自己对文件末尾的记录。你可以通过文件上锁的方式来避免冲突。(第七章将具体描述这些内容)
将被打开的文件的名字——路径——会作为参数传入,oflags参数用来指定打开这个文件需要执行的行为(action)。
oflags参数用来指定文件访问(access)模式或其他相关的模式。open方法必须指定以下表中展示的访问模式中的一个。
通过组合的方式(用 | OR符)加入以下可选的模式:
[align=left]O_APPEND :将写入的数据放在文件末尾[/align]
[align=left]O_TRUNC :将文件的长度设为0,删除已存在的内容[/align]
[align=left]O_CREAT :创建文件,如果需要,和权限[/align]
[align=left]O_EXCL :和O_CREAT,确保调用者创建文件。open是原子性的(atomic)也就是说,只有一个函数调用。这保护了两个程序同时创建一个文件。如果有一个成功了,另一个将会失败。[/align]
[align=left]oflags的其他取值在用户手册中有文档描述——文档的第二章。[/align]
[align=left]如果打开成功,则open方法返回值是文件描述符,如果失败,则返回-1,具体的错误信息通过查看全局变量errno来获知。最新的文件描述符总是所有未使用的描述符里值最小的,在某些情况下,这种特征很有有。例如,如果程序关闭了标准输出,然后再调一次open,这描述符1将被重用,原来指向标准输出的描述符1,将指向另一个文件或者设备。[/align]
使用create方法也是符合POSIX标准的,但是不常用。create不仅仅创建,而且会打开。它跟调用open并使传入参数oflags的值O_CREAT
| O_WRONLY | O_TRUNC 的效果是一样的。
一个运行中的程序所能打开的最大文件数目是有限的。这个限制,总是会在limit.h头文件中以OPEN_MAX常量来表明,并在不同的系统中值也不同,但是POSIX规定了这个值最小是16。这个限制可能本身还需要服从整个系统的限制,所以并不一定能达到这个数目。在Linux下,限制会在运行时发生改变,所以OPEN_MAX是一个变量。这个值一般从256开始。
File Access
每一个运转的程序——也叫进程——都会拥有一些相关联的文件描述符(file descriptor)。这是些不大的整数,可以用来使用打开了的文件或者设备。有多少个文件描述符可以使用,取决于系统如何配置。当一个程序开始运行时,通常有3个文件描述符已经打开。它们是:
0:标准输入
1:标准输出
2:标准错误流
你可以通过使用系统调用open,关联其他文件指示符到文件以及设备。这些已经被自动打开的文件指示符,允许你用write来创建程序。
write
系统调用write将把指针buf(write方法的参数)所指向的内存中的前nbyte(write方法的参数)个字节写入到文件描述符fildes(write方法的参数)所关联的文件中。返回的值为实际写入的字节数。这个值可能会小于nbyte,如果文件描述符有错,或者底层设备对数据块大小敏感。如果返回0,表示没有写入,如果返回-1,表示有错,错误的信息将从全局变量errno获得。
以下为语法:
#include <unistd.h>
size_t write(int fildes, const void *buf, size_t nbytes);
可以写一个程序, simple_write.c来试试:
#include <unistd.h>
#include <stdlib.h>
int main()
{
if ((write(1, “Here is some data\n”, 18)) != 18)
write(2, “A write error has occurred on file descriptor 1\n”,46);
exit(0);
}
这个程序简单地print一句信息到标准的输出流中。当程序退出时,所有的打开的文件描述符将被自动关闭,所以不需要显式地关闭。然而,当你使用的是缓冲的输出流时,根本就不用考虑这个。
$ ./simple_write
Here is some data
$
值得再次注意的是,write可能返回的字节数小于你指定字节数。这并不一定是错误。在你的程序中,你需要检查errno,然后写入剩余的数据。
read
read方法将从文件描述符fildes关联的文件中,读取nbytes个字节的数据,并放入到指针buf所指向的内存中。返回的值为所读取的真实的字节数,可能少于nbytes。如果返回0,表示没有读到任何数据,已经到达文件的末尾了。如果出错,这返回-1.
[align=left]#include <unistd.h>[/align]
[align=left]size_t read(int fildes, void *buf, size_t nbytes);[/align]
以下程序,simgle_read.c,将128个字节标准输入复制到标准输出中。如果输入的字节少于128,则将全部复制到输出流中。
[align=left]#include <unistd.h>[/align]
[align=left]#include <stdlib.h>[/align]
[align=left]int main()[/align]
[align=left]{[/align]
[align=left]char buffer[128];[/align]
[align=left]int nread;[/align]
[align=left]nread = read(0, buffer, 128);[/align]
[align=left]if (nread == -1)[/align]
[align=left]write(2, “A read error has occurred\n”, 26);[/align]
[align=left]if ((write(1,buffer,nread)) != nread)[/align]
[align=left]write(2, “A write error has occurred\n”,27);[/align]
[align=left]exit(0);[/align]
[align=left]}[/align]
如果执行这个文件,将看到如下输出:
[align=left]$ echo hello there | ./simple_read[/align]
[align=left]hello there[/align]
[align=left]$ ./simple_read < draft1.txt[/align]
[align=left]Files[/align]
[align=left]In this chapter we will be looking at files and directories and how to manipulate[/align]
[align=left]them. We will learn how to create files,$[/align]
在第一次执行中,你通过echo输入了内容到输入流中,然后通过管道符,传到了你的文件中。在第二次执行中,你从一个文件中导入了数据到输入流中,你可以看到输出的只是文件中的前部分内容。
可以注意到shell命令符紧跟在输出文字的最后一位,在这个文件中,128个字符没有产生所有的内容。
open
要产生新的文件描述符,需要使用open方法。
[align=left]#include <fcntl.h>[/align]
[align=left]#include <sys/types.h>[/align]
[align=left]#include <sys/stat.h>[/align]
[align=left]int open(const char *path, int oflags);[/align]
[align=left]int open(const char *path, int oflags, mode_t mode);[/align]
严格上讲,在POSIX标准下,你不需要include sys/types.h、sys/stat.h文件来使用open方法。当在某些UNIX系统下还是需要的。
简单地说,open创建了使用文件或者设备的途径。如果成功,返回文件描述符,然后可以用于read, write, 以及其他系统调用。文件描述符是独一无二的,并且不会与其他进程共享同一个描述符。如果两个程序同时打开了同一个文件,他们使用的是不一样的文件描述符。如果他们都写入数据到文件,则他们会继续写入到之前离开的位置。写入的数据不会交错存取,而是会覆盖掉另一个的数据。两个程序会各自维护自己对文件末尾的记录。你可以通过文件上锁的方式来避免冲突。(第七章将具体描述这些内容)
将被打开的文件的名字——路径——会作为参数传入,oflags参数用来指定打开这个文件需要执行的行为(action)。
oflags参数用来指定文件访问(access)模式或其他相关的模式。open方法必须指定以下表中展示的访问模式中的一个。
Mode | Description |
O_RDONLY | [align=left]Open for read-only[/align] |
O_WRONLY | Open for write-only |
O_RDWR | Open for reading and writing |
[align=left]O_APPEND :将写入的数据放在文件末尾[/align]
[align=left]O_TRUNC :将文件的长度设为0,删除已存在的内容[/align]
[align=left]O_CREAT :创建文件,如果需要,和权限[/align]
[align=left]O_EXCL :和O_CREAT,确保调用者创建文件。open是原子性的(atomic)也就是说,只有一个函数调用。这保护了两个程序同时创建一个文件。如果有一个成功了,另一个将会失败。[/align]
[align=left]oflags的其他取值在用户手册中有文档描述——文档的第二章。[/align]
[align=left]如果打开成功,则open方法返回值是文件描述符,如果失败,则返回-1,具体的错误信息通过查看全局变量errno来获知。最新的文件描述符总是所有未使用的描述符里值最小的,在某些情况下,这种特征很有有。例如,如果程序关闭了标准输出,然后再调一次open,这描述符1将被重用,原来指向标准输出的描述符1,将指向另一个文件或者设备。[/align]
使用create方法也是符合POSIX标准的,但是不常用。create不仅仅创建,而且会打开。它跟调用open并使传入参数oflags的值O_CREAT
| O_WRONLY | O_TRUNC 的效果是一样的。
一个运行中的程序所能打开的最大文件数目是有限的。这个限制,总是会在limit.h头文件中以OPEN_MAX常量来表明,并在不同的系统中值也不同,但是POSIX规定了这个值最小是16。这个限制可能本身还需要服从整个系统的限制,所以并不一定能达到这个数目。在Linux下,限制会在运行时发生改变,所以OPEN_MAX是一个变量。这个值一般从256开始。
相关文章推荐
- 使用文件系统调用
- 用php 执行PhantomJS文件,php中使用exec,system等函数调用系统命令
- B/S(WEB)系统中使用Activex插件调用扫描仪实现连续扫描并上传图像(IE文件扫描并自动上传)
- h5 app开发使用cordova调用相册、相机、文件系统或录音录视频,并上传到服务器
- 文件拷贝函数(使用系统调用和C库函数)
- linux下使用系统调用读取文件中的路径
- 使用系统调用fcntl()来对文件加锁
- C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理
- IE下使用js调用系统activex实现文件下载
- 用python处理文本,本地文件系统以及使用数据库的知识基础
- Linux汇编教程13:系统调用和文件处理上
- 13.7 混合使用库函数和系统调用进行文件I/O
- 使用Web程序,调用Bat自动处理文件
- B/S(WEB)系统中使用Activex插件调用扫描仪实现连续扫描并上传图像(IE文件扫描并自动上传)
- Android客户端之“微服私访”App的系统学习(八)调用系统摄像头拍照并管理照片并使用Okhttp上传文件至后台
- QT5删除隐藏目录+隐藏文件(使用Process::start函数调用系统命令,且等待到结束)
- 2010-07-21 使用系统调用实现文件复制
- lamp使用php处理上传文件,调用move_uploaded_file函数遇到目录写权限问题及解决过程
- java使用JNA调用系统Kernel32.dll文件示例
- 使用Intent调用系统其它程序打开本地各种类型的文件