文件描述符

在 Linux 的世界里,一切设备皆文件。我们可以系统调用中 I/O 的函数(I:input,输入;O:output,输出),对文件进行相应的操作( open()、close()、write() 、read() 等)。

打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件

程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符 0、1、2 记录在表中。程序运行起来后这三个文件描述符是默认打开的

#define STDIN_FILENO  0 //标准输入的文件描述符

#define STDOUT_FILENO 1 //标准输出的文件描述符

#define STDERR_FILENO 2 //标准错误的文件描述符

在程序运行起来后打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。Linux 中一个进程最多只能打开 NR_OPEN_DEFAULT (即1024)个文件,故当文件不再使用时应及时调用 close() 函数关闭文件。

常用 I/0 函数

需要的头文件:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

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

功能:

打开文件,如果文件不存在则创建。

参数:

pathname: 文件的路径及文件名。

flags: 打开文件的行为标志,如,以只读方式(O_RDONLY,第一个为字母不是数据)打开,以读写或新建新文件的方式(O_RDWR|O_CREAT)打开。

mode: 这个参数,只有在文件不存在时有效,指新建文件时指定文件的权限(文件权限详情,请点此链接)。

返回值:

成功:成功返回打开的文件描述符

失败:-1

int close(int fd);

功能:

关闭已打开的文件

参数:

fd: 文件描述符,open()的返回值

返回值:

成功:0

失败:-1

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

功能:

把指定数目的数据写到文件(fd)

参数:

fd: 文件描述符

addr: 数据首地址

count: 写入数据的长度(字节),一般情况下,数据有多少,就往文件里写多少,不能多也不能少

返回值:

成功:实际写入数据的字节个数

失败:-1

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

功能:

把指定数目的数据读到内存(缓冲区)

参数:

fd: 文件描述符

addr: 内存首地址

count: 读取的字节个数

返回值:

成功:实际读取到的字节个数

失败:-1

实战示例

接下来,我们使用以上 4 个系统调用 I/O 函数写一个程序,能实现像系统命令 cp 的功能:

使用 open() 打开源文件,使用 read() 从文件读数据,使用 write() 向目的文件写数据,示例代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{if((argc == 3) && (strcmp(argv[1], argv[2]) != 0)){// 保证有 3 个参数,而且源文件和目的文件名字不能一样int fd_src, fd_dest, ret;//只读方式打开源文件fd_src = open(argv[1], O_RDONLY); if(fd_src < 0){perror("open argv[1]");return -1;}// 新建目的文件fd_dest = open(argv[2], O_WRONLY|O_CREAT, 0755);if(fd_dest < 0){close(fd_src);perror("open argv[2]");return -1;}do{char buf[1024] = {0};// 从源文件读取数据ret = read(fd_src, buf, sizeof(buf));// 把数据写到目的文件,注意最后一个参数,有多少写多少write(fd_dest, buf, ret);}while(ret > 0);// 关闭已打开的文件close(fd_src);close(fd_dest);}return 0;
}

运行结果如下:

文件IO

1. 引言
大多数LInux文件IO只需用到5个函数:open read write lseek close.

2. 文件描述符
对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,
用open或create返回的文件描述符标识该文件,将其作为参数传送给read或write。
在POSIX.1应用程序中,幻数0、1、2应被代换成符号常数STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO。这些常数都定义在头文件<unistd.h>中。
每个进程运行时,系统已分配3个文件描述符(0,1,2),分别对应标准输入, 标准输出, 标准错误输出

3. 文件IO中常用的函数

3.1 open: 
打开一个文件或者设备文件, 返回一个文件描述符。当操作此文件描述符时,就是操作相应的文件或设备
int open(char *pathname,int falgs,.../*mode*/)
flags 必须指其中O_RDONLY  O_WRONLY  O_RDWR唯一项
可选项:
O_APPEND   每次写操写都将文件指针定位文件尾处
O_CREAT    如果文件不存在创建新文件
O_EXCL     如指定O_CREAT时文件存在 出错返回
O_TRUNC    必须以O_WRONL或O_RDWR进行操作,把文件清空

O_NONBLOCK 以非阻塞的方式打开 
O_NOCTTY   如果打开文件为终端设备,不将该设备分配为此进程的控制终端
O_SYNC     每次write都等到I/O操作完成,并等文件的属性更新
O_RSYNC    作为read操作等侍,直到任何对同文件部分未决的写入完成
O_DSYNC    每次write都等到I/O操作完成,不等文件的属性更
mode指定创建文件的权限
//创建一个文件,假设这个文件存在时要清空
open("文件", O_RDWR|O_CREAT|O_TRUNC, 0777);
fd = open("txt", O_RDONLY | O_CREAT | O_EXCL, 0644);

3.2 creat
   int creat(char *pathname,mode_t mode)
   等价于:open(char *,O_WRONLY | O_CREAT | O_TRUNC, mode)

3.3 read
读取已经打开的文件中的数据。读了文件以后,文件描述符对应文件的offset会自动偏移。
ssize_t read(int fd, void *buf, size_t count);
从文件描述符fd指向文件里读取最大为count字节的数据放到buf指定的地址上去.
返回值: 为实际上读取的数值, 为0时读到文件尾, 为-1时错误

3.4 write
向指定的文件中写数据。成功写以后, 文件描述符的offset自动偏移
size_t  write(int fd, const void *buf, size_t count);
把在buf地址指定的数据写到fd指向的文件里,最大写count字节.
成功返回写了多少字节, -1失败。write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。

3.5 lseek
每个打开文件都有一个与其相关联的“当前文件位移量”。它是一个非负整数,用以度量
从文件开始处计算的字节数。就是改变文件描述符的偏移位置。
off_t lseek(int fildes, off_t offset, int whence);
whence:
SEEK_SET : 把偏移量设为offset, 从文件头开始.
SEEK_CUR : 把当前偏移量加上offset的值
SEEK_END : 先从文件尾开始偏移offset的值

返回值:成功返回定位之后的文件指针偏移 失败返回 -1
返回当前文件的偏移量
off_t currpos = lseek(fd, 0, SEEK_CUR);
3.7 close
把没用的文件描述符关掉,把此文件描述符重新分配.
int close(int fd);

3.8 dup
可用来复制一个现存的文件描述符.
int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup2(oldfd, newfd) //让newfd成为oldfd的一个副本
dup2(fd, 1); //让fd替代标准输出

int ret=dup2(old,new)
如果new 打开的,则关闭new 返回新的文件描述符 失败返回-1. du2是一个原子操作。
dup2可以用newfd参数指定新描述符的数值。如果newfd当前已经打开,则先将其关闭再做dup2操作,如果oldfd等于newfd,则dup2直接返回newfd而不用先关闭newfd再复制。

3.9 fcntl 
用于改变已打开的文件的性质 只能改变O_APPEND, O_NONBLOCK,O_ASYNC,  O_DIRECT
int fcntl(int fileds,int cmd,.../*int arg */)
第三个参数除了cmd用于记录锁时为一个结构指针之外,其余均为整数

fcntl五种功能如下:
.复制一个现有的描述符 cmd=F_DUPFD
复制的新文件描述符清掉文件描述符标志 并且共享同一个文件表项

.获得/设置文件描述符标记 cmd=F_GETFD/F_SETFD
flags = fcntl(fd, F_GETFL); //获取
fcntl(fd, F_SETFL, flags);  //设置

.获得/设置文件状态标志 cmd=F_GETFL/F_SETFL
 忽略 O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC标志
 由于历史原因O_RDONLY O_WRONLY O_RDWR并不各占一位,
 它们之间互斥。因此首先必须用屏蔽字O_ACCMODE 
 取得访问模式然后与这三种flags比较
 value=fcntl(fd,F_GETFL,0);
 switch(value & O_ACCMODE){
case O_RDONLY :
 }

.获得/设置导步I/O所有权cmd=F_GETOWN/F_SETOWN
.获得/设置记录锁 cmd=F_GETLK F_SETLK/F_SETLKW
   
3.10 pread  pwrite  
定位文件进行读写 不影响文件指针 偏移和读写操作为原子操作

3.11 sync
   void sync(void)     
    :函数只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等实际写磁盘完成
   int fsync(int fd) 
    :等待实际磁盘写操作完成,并且同步更新文件的属性,可用于数据库类型的应用程序
   int fdatasync(int fd)
    :类似于fsync,但只影响文件的数据部分不影响文件的属性
   
3.12 ioctl 
int ioctl(int fd, int request, ...);
    称之为I/O操作的垃圾箱 只要其字操函数不能或难于实现在 它都可能很容易做到
==========================================================
4. 某些系统下提供名为/dev/fd/N 等文件。打开文件/dev/fd/N 等效于复制N文件描述符(假定N描述符是打开的)
与其N共享文件表项
也有某些系统为/dev/fd/stdin   /dev/fd/stdout 等,均为同等操作

homework:
1.  实现mycopy拷贝一个文件到另外一个文件(功能相当于 cp a.txt b.txt)
2.  实现mytouch 创建一个文件(功能相当于touch a.txt)。
3.  编写一个同dup2功能相同的函数,要求不调用fcntl函数并且要有正确的出错处理。
4.  在如启用添加标志打开一文件以便读、写,能否用lseek在任一位置开始读?能否用lseek更新文件中任一部分的数据?请写一段程序以验证之。

【Linux系统编程】文件IO操作相关推荐

  1. Linux系统编程-文件的操作

    Linux系统编程-文件操作 前言: Linux 中所有内容都是以文件的形式保存和管理的,即一切皆文件,普通文件是文件,硬件设备(键盘.监视器.硬盘.打印机)是文件,就连套接字(socket).网络通 ...

  2. 外网访问arm嵌入式linux_嵌入式Linux系统编程——文件读写访问、属性、描述符、API

    Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...

  3. 4. linux调用文件计算阶乘前5项和_嵌入式Linux系统编程——文件读写访问、属性、描述符、API

    Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...

  4. linux系统编程——文件编程

    linux系统编程--文件编程 文章目录 linux系统编程--文件编程 一.如何创建.修改一个文件 二.linux系统调用的API 三.文件描述符 四.linux系统调用 1.创建文件-creat ...

  5. linux原子过程,linux系统编程:IO读写过程的原子性操作实验

    所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...

  6. linux 文件操作 编程,Linux系统编程------------文件操作(基础)

    一.文件操作 1.1 Linux文件系统结构 1.1.1  Linux常见系统目录 /bin  :  存放普通系统可执行的命令(ls wc等) /sbin  :  存放系统管理程序(fsck等) /b ...

  7. Linux基础(6)--文件IO操作

    文件IO操作 1. open打开操作 2. close关闭操作 3. creat创建操作 4. write写操作 5. read读操作 Linux下一切皆文件,所以文件IO是很重要的也是很基础的操作. ...

  8. 嵌入式Linux应用开发 1.系统编程 文件IO:open close write read lseek 通过文件io实现cp命令

    跟着b站边学边记,加上自己的理解和代码的测试,也算是给自己做个笔记. 1.使用linuxIO和我们直接写程序的区别 我们平常直接写程序,属于在应用层写程序,通过我们的printf传入内核(在这里pri ...

  9. Linux系统编程@文件操作(一)

    只总结了部分常用的内容,详细内容参考<UNIX环境高级编程>及相关书籍. Linux中文件编程可以使用两种方法 Linux系统调用(依赖于系统) C语言库函数(不依赖于系统) Linux系 ...

  10. Linux编程——文件 IO操作【转】

    (转自:https://blog.csdn.net/baidu_28312631/article/details/47828711) Linux文件 I\O 介绍     1. Linux系统调用   ...

最新文章

  1. AlarmManager深入浅出
  2. 中小型研发团队架构实践:电商如何做企业总体架构?
  3. 视觉SLAM学习--相机成像模型及标定
  4. CF628D Magic Numbers (数据大+数位dp)求[a,b]中,偶数位的数字都是d,其余为数字都不是d,且能被m整除的数的个数...
  5. redis应用之——注册、登录
  6. GM6 PageSet request didn't have target application url
  7. 《Oracle RAC 高并发系统的故障诊断》直播资料大放送
  8. tensorflow之FIFOQueue
  9. Web SQL本地数据库(SQLLite)
  10. 机器人学导论复习笔记
  11. JAVA创建内部类对象
  12. stm32学习----正电原子精英板控制电机正反转
  13. PYTHON通过高德API实现城市地址与经纬度批量转换(每十个一组查询)
  14. [CVPR2021]NeRV: Neural Reflectance and Visibility Fields for Relighting and View Synthesis
  15. TCGA/癌症基因组图谱数据库|肿瘤简写中英文对照
  16. python:输出10行的杨辉三角 - 二项式的n次方展开系数
  17. win10 的 PS 不能直接拖进文件的解决方法(附:与 Edge 登录的冲突)
  18. 关于微信小程序在部分PC设备无法打开的问题
  19. Linux qt教程 qt入门(一)
  20. 概率论的一些基本概念

热门文章

  1. 前端的date类型后台接收_腾讯高级前端工程师支招,云开发实现小程序打赏和提现云开发实践...
  2. 7-72 分解质因数 (20 分)
  3. C语言学习之输出10个整数中的最大值及其下标、最小值及其下标
  4. freebsd 手工安装zabbix2.0 php,zabbix 服务端,子客户端安装配置日志
  5. 正态分布图_用EXCEL简易制作正态分布图
  6. 浙江绿盟科技2011.10.14校园招聘会笔试题
  7. bash: /opt/hisi-linux/x86-arm/arm-hisiv300-linux/target/bin/arm-hisiv300-linux-gcc: 没有那个文件或目录。...
  8. Codeforces Beta Round #1 A,B,C
  9. 编码 Unicode utf-8
  10. TabActivity中的Tab标签详细设置