2. 文件I/O系统调用及文件描述符

2.1 文件I/O系统调用

(1)主要函数

函数

功能

函数

功能

open()

打开文件

read()

读取文件

creat()

创建文件

write()

写入文件

close()

关闭文件

lseek()

文件定位

注意

这些不带缓存的函数都是内核提供的系统调用。它们不是ANSI C的组成部分,但是POSIX的组成部分

(2)系统调用与C库

(3)文件操作方式

  ①标准库函数:遵守ISO标准,基于流的I/O,对文件指针(FILE结构体)进行操作。

  ②系统调用(API方式):兼容POSIX标准,基于文件描述符的I/O,对文件描述符进行操作。

2.2 文件描述符

(1)概念:

  ①对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用open或creat返回的文件描述标识该文件,将其作为参数传递给read或write。

  ②在POSIX应用程序中,整数0、1、2被替换成符号常数STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO。这些常量都定义在头文件<unistd.h>中。

  ③文件描述符的范围是0-OPEN_MAX。早期的UNIX版采用的上限值是19(允许每个进程打开20个文件)。现在很多系统则将其增加至63,Linux为1024。

(2)文件描述符与文件指针

  ①标准文件指针:stdin(0)、stdout(1)、stderr(2)

  ②将文件描述符转为文件指针FILE* fdopen(int fd, const char* mode)

  ③将文件指针转为文件描述符int fileno(FILE* stream);

2.3 文件I/O系统调用

(1)open函数

头文件

#include<sys/types.h>  //位于/usr/include目录下

#include<sys/stat.h>   //文件状态信息,如大小,创建时间,UID等。

#include<fcntl.h>    //file control

函数

int open(const char* pathname, int flags);

int open(const char* pathname,int flags, mode_t mode);

返回值

若成功为文件描述符,若出错为-1

功能

打开或创建一个文件

参数

(1)pathname:要打开或者创建的文件路径

(2)flags:用来说明此函数的多个选择项

  ①O_RDONLY  只读

  ②O_WRONLY  只写

  ③O_RDWR    读写

  ④O_APPEND:以追加模式打开文件,每次写时都加到文件的尾端,但在网络文件系统进行操作时却没有保证。

  ⑤O_CREAT:如果指定文件不存在,则按照mode参数指定的文件权限来创建文件。

  ⑥O_EXCL:如果同时指定了O_CREAT,而文件己经存在,则出错这可测试一个文件是否存在)。但在网络文件系统进行操作时却没有保证。

  ⑦O_DIRECTORY:如果参数pathname不是一个目录,则open出错。

  ⑧O_TRUNC:如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。

  ⑨O_NONBLOCK:如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。

(3)mode:新建文件的访问根限(如0777),对于open函数而言,仅当创建新文件时才使用第三个参数。

(2)creat函数

头文件

#include<sys/types.h>  //位于/usr/include目录下

#include<sys/stat.h>   //文件状态信息,如大小,创建时间,UID等。

#include<fcntl.h>    //file control

函数

int creat(const char* pathname, mode_t mode);

返回值

若成功为文件描述符,若出错为-1

功能

创建一个新文件

备注

(1)此函数等效于:open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);

(2)creat的一个不足之处是它以只写方式打开所创建的文件。

(3)close函数

头文件

#include<unistd.h>    //unix std文件

函数

int close(int fd);

返回值

若成功为0,若出错为-1

功能

关闭一个打开的文件

参数

fd:己打开的文件描述符;

备注

当一个进程终止时,它所有的打开文件都由内核自动关闭。

(4)read函数

头文件

#include<unistd.h>

函数

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

返回值

读到的字节数,若己到文件尾为0,若出错为-1

功能

从打开文件中读数据

参数

(1)fd:读取文件的文件描述符。

(2)buf:存放读取数据的缓存。

(3)count:要求读取一次数据的字节数

备注

(1)有多种情况可使实际读到的字节数少于要求读取的字节数。

  ①读普通文件时,在读到要求字节数之前己经到达文件尾端。

  ②当从终端设备读时,通常一次最多读一行。

  ③当从网络读时,网络中的缓存机构可能造成返回值小于所要求读的字节数。

  ④某些面向记录的设备,例如磁带,一次最多返回一个记录。

  ⑤进程由于信号造成中断。

(2)读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数

(5)write函数

头文件

#include<unistd.h>

函数

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

返回值

成功为己写的字节数,若出错为-1

功能

向打开文件中写数据

参数

(1)fd:写入文件的文件描述符。

(2)buf:存放待写数据的缓存。

(3)count:要求写入一次数据的字节数

备注

(1)其返回值通常与参数count的值相同,否则表示出错。

(2)write出错的一个常见原因是:磁盘己写满,或者超过了对一个给定进程的文件长度限制。

(3)对于普通文件,写操作从文件的当前位移量开始。如果在打开该文件时,指定了O_APPEND选项时,则在每次写操作之前,将文件位移量设置在当前文件的结尾处。在一次成功写入后,该文件位移量增加实际写的字节数。

【编程实验】读写文件及判断文件大小

//io.h

#ifndef __IO_H__
#define __IO_H__ extern void copy(int fdin, int fdout); #endif

//io.c

#include "io.h"
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h> //单独编译命令:gcc -o obj/io.o -Iinclude -c src/io.c #define BUFFER_LEN 1024 //与分区文件块大小一致。可以通过
//tune2fs -l /dev/sda1命令查看 void copy(int fdin, int fdout)
{
char buffer[BUFFER_LEN]; ssize_t size; //保证从文件开始处复制
lseek(fdin, 0L, SEEK_SET);
lseek(fout, 0L, SEEK_SET); while((size = read(fdin, buffer, BUFFER_LEN)) > ){
if(write(fdout, buffer, size) != size)
{
fprintf(stderr, "write error: %s \n", strerror(errno));
exit();
}
} if (size < )
{
fprintf(stderr, "read error: %s\n",strerror(errno));
exit(); //return 1;
}
}

//copy_test.c

#include "io.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h> //程序功能:实现文件的复制
//编译命令:gcc -o bin/cp -Iinclude obj/io.o src/copy_test.c int main(int argc, char* argv[])
{ if(argc != ){
fprintf(stderr, "usage: %s srcfile destfile\n", argv[]);
exit();
} int fdin, fdout; //打开一个待读取的文件
fdin = open(argv[], O_RDONLY);
if(fdin < )
{
fprintf(stderr, "open error:%s\n", strerror(errno));
exit();
}else{
printf("open file: %d\n", fdin);
}
//输出源文件的大小(利用lseek系统调用)
printf("source file length: %ld\n", lseek(fdin, 0L, SEEK_END)); //创建一个新文件,并指定文件的权限为0777
fdout = open(argv[], O_WRONLY | O_CREAT | O_TRUNC, );
if(fdout < )
{
fprintf(stderr, "open error:%s\n", strerror(errno));
exit();
}else{
printf("open file: %d\n", fdout);
} //利用自定义的copy函数实现文件的拷贝
copy(fdin, fdout); //输出目录文件的大小
printf("destination file length: %ld\n", lseek(fdin, 0L, SEEK_END)); close(fdin);
close(fdout); return ;
}

(6)lseek函数

头文件

#include<sys/types.h>

#include<unistd.h>

函数

off_t lseek(int fd, off_t offset, int whence);

返回值

若成功则返回新的文件位移量(绝对偏移量),若出错为-1

功能

定位一个己打开的文件(会改变文件的当前偏移量

参数

(1)fd:己打开文件的文件描述符。

(2)offset:位移量。

(3)whence:定位的位置

  ①SEEK_SET:将该文件的位移量设置为文件开始处offset个字节处。offset只能为正

  ②SEEK_CUR:将该文件的位移量设置为当前值加offset,offset可正可负

  ③SEEK_END:将该文件的位移量设置为文件长度加offset,offset可正可负。当offset为正时,表示在定位到文件末尾再加offset字节处,其中会出现一些“空洞”,这种文件也叫“空洞”文件

备注

(1)lseek也可用来确定所涉及的文件是否可以设置位移量。如果文件描述符引用的是一个管道或FIFO,则lseek返回-1,并将errno设置为EPIPE。

(2)每个打开文件都有一个与其相关联的“当前文件偏移量”。它是一个非负整数,用以度量从文件开始处计算的字节数。通常读、写操作都从当前文件偏移量处开始,并使偏移量增加所读或写的字节娄和。按系统默认,当打开一个文件时,除非指定O_APPEND选项,否则该位置被设置为0。

(3)“空洞文件大小”含空洞部分的大小 ,但实际占用的物理磁盘块大小为整个文件除去空洞部分以外的内容占用的空间大小

【编程实验】创建“空洞”文件

//hole_file.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h> //生成一个“空洞”文件(可用“od -c 文件名”命令来查看这个文件!) char* buffer = ""; int main(int argc, char* argv[])
{
if(argc < ){
fprintf(stderr, "usage: %s [file]\n", argv[]);
exit();
} int fd = open(argv[], O_WRONLY | O_CREAT | O_TRUNC, );
if(fd < ){
fprintf(stderr, "open error: %s\n", strerror(errno));
exit();
} //计算要写入的字符串所占空间大小
size_t size = strlen(buffer)* sizeof(char); //将buffer中的内容写入文件
if(write(fd, buffer, size) != size){
perror("write error");
exit();
} //定位到文件尾部的10个字节处(注意,不是文件尾部,而是末尾再跳过10字节)
//即当中出现了“空洞”
if(lseek(fd, 10L, SEEK_END) < ){
perror("lseek error");
} //再次写入上述字符串
if(write(fd, buffer, size) != size)
{
perror("write error");
exit();
} close(fd); return ;
}

第3章 文件I/O(2)_文件I/O系统调用及文件描述符的更多相关文章

  1. [一]FileDescriptor文件描述符 标准输入输出错误 文件描述符

    文件描述符   当应用程序请求打开或者操作文件时,操作系统为应用程序设置一张文件列表,具体的实现形式此处不深入说明 操作系统会提供给你一个非负整数,作为一个索引号,它的作用就像地址或者说指针或者说偏移 ...

  2. Linux中通过Socket文件描述符寻找连接状态介绍

    针对下文的总结:socket是一种文件描述符 进程的打开文件描述符表 Linux的三个系统调用:open,socket,pipe 返回的都是一个描述符.不同的进程中,他们返回的描述符可以相同.那么,在 ...

  3. 终端设备 tty,pty,pts 概念与文件描述符的联系

    第1节 理解终端设备tty.pty.pts概念 简要描述: 终端设备默认具有输入.输出功能. 现代我们最常用的接入服务器端的方式(如:ssh通过tcp/ip的方式连接服务器端,作为服务器的终端设备)为 ...

  4. 18 shell 重定向以及文件描述符

    1.对重定向的理解 2.硬件设备和文件描述符 文件描述符到底是什么 3.Linux Shell 输出重定向 4.Linux Shell 输入重定向 5.结合Linux文件描述符谈重定向 6.Shell ...

  5. 第17章 内存映射文件(3)_稀疏文件(Sparse File)

    17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...

  6. 第9章 应用层(5)_文件传输协议FTP

    6. 文件传输协议FTP 6.1 FTP主动和被动模式 (1)FTP协议 ①与其他协议不同,FTP协议在客户端访问FTP服务器时需要建立两个TCP连接.一个用来传输FTP命令,一个用来传输数据. ②在 ...

  7. Asp.Net Web Api 2 实现多文件打包并下载文件示例源码_转

    一篇关于Asp.Net Web Api下载文件的文章,之前我也写过类似的文章,请见:<ASP.NET(C#) Web Api通过文件流下载文件到本地实例>本文以这篇文章的基础,提供了Byt ...

  8. linux文件内容列传行_行转列

    ================ 文件内容列传行_行转列  ================ 一.列转行 1.编辑测试文件 vi log.txt 16:23:00 8.2% 1773620k 16:2 ...

  9. 字符输出流_Writer类&FileWriter类介绍和字符输出流的基本使用_写出单个字符到文件

    java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类 共性的成员方法: - void write(int c) 写入单个字符 - void write(char[] ...

随机推荐

  1. windows开机自动登录

     控制台输入control userpasswords2 ,去掉下图中的√,输入登陆所用用户名密码即可.

  2. HRBUST单词接龙

    题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合 ...

  3. C语言基础:指针初级(补充) 分类: iOS学习 c语言基础 2015-06-10 21:54 19人阅读 评论(0) 收藏

    结构体指针:指向结构体指针的变量的指针. 结构体指针指向结构体第一个成员变量的首地址 ->:   指向操作符 定义的指针变量必须指向结构体的首地址,才可以使用  ->  访问结构体成员变量 ...

  4. 详解基本TCP套接字函数

    以下讲解基本TCP套接字函数. 1.socket 函数   指定期望的通信协议类型.    #include <sys/types.h> /* See NOTES */ #include  ...

  5. Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet

    /*********************************************************************************** * * jiffies,tim ...

  6. stm32 SPI介绍和配置

    SPI是一种高速的,全双工同步的通信总线,在芯片管脚上占用了四根线,节约了芯片的管脚,同时为PCB的布局节省了空间,提供了方便,因此越来越多的芯片集成了这种通信协议,STM32也就有了SPI接口. 有 ...

  7. 微信小程序实现多选分享

    微信小程序拉取好友列表后,默认只能选一个分享,现在在分享回调onShareAppMessage里加上这段代码,拉取好友列表时,右上角会出现多选按钮,这样就解决了微信小程序安卓下只能单选分享的问题. / ...

  8. 网络流初步:<最大流>——核心(增广路算法)(模板)

    增广路的核心就是引入了反向边,使在进行道路探索选择的时候增加了类似于退路的东西[有一点dp的味道??] 具体操作就是:1.首先使用结构体以及数组链表next[ MAXN ]进行边信息的存储 2.[核心 ...

  9. UVA10590 Boxes of Chocolates Again

    题意 将正整数N拆分成若干个正整数之和,问有多少种不重复的拆分方案. \(n \leq 5000\) 分析 用f(i,j)表示将i拆成若干个数字,最大的那个数字(即最后一个数)不超过j的方案数. 转移 ...

  10. loopback v4 特性

    loopback 是一个api 服务框架,挺方便的,同时也已经演进了好几代了v4 有一些新功能的 支持 新特性 基于typescript/es2017 开发 openapi 驱动的rest api 开 ...