linux 文件I/O教程

  一,文件描述符

  对内核而言,所以打开的文件都通过文件描述符引用。每个进程都有一些与之关联的文件描述符。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读或写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传送给read和write。

  一般有三个以及打开的文件描述符,他们是:

  兄弟连Linux培训

复制代码代码如下:

  0:标准输入 STDIN_FILENO

  1:标准输出 STDOUT_FILENO

  2标准错误输出 STDERR_FILENO

  每行后面的符号常量是依从POSIX而定的。

  open函数

  复制代码代码如下:

  #include<sys/types.h>

  #include <sys/stat.h>

  #include <fcntl.h>

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

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

  pathname是要打开或创建文件的名字。

  flag用来定义打开文件所采取的的动作,必须调用以下模式之一

  O_RDONLY, O_WRONLY, O_RDWR分别代表只读,只写,读写方式打开。

  open还可以包括以下可选模式的组合

  O_APPEND:把写入数据追加到文件的尾端

  O_CREAT:若文件不存在,则创建它。使用此选项时,需要第三个参数mode,用其指定该新文件的访问权限。

  O_EXCL:如果同时指定了O_CREAT,而文件存在,则会出错。用此可以测试一个文件是否存在,如果存在,则创建文件,这使测试和创建两者成为一个原子操作。

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

  open返回的文件描述符一定是最小的未用描述符数值。这一点被某些应用程序用在标准输入,标准输出或标准错误输出上。如,一个程序关闭了自己的标准输出,然后再次调用open,文件描述符1就会被调用,并且标准输出将被有效的重定向到另一个文件或设备。

  POSIX规范还标准化了一个creat调用,此函数等效于

  open(pathname,O_WONLY |O_CREAT | O_TRUNC, mode);

  close函数

  #include <unistd.h>

  int close(int fd);

  close调用终止一个文件描述符fd与对应文件之间的关联。文件描述符被释放后并能重新使用。close调用成功返回0,出错返回-1.

  关闭一个文件时会释放该进程加在文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有打开的文件。

  lseek函数

  每个打开的文件都有一个与其相关联的”当前文件偏移量”。按系统默认情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。lseek可以为一个打开的文件设置偏移量。

  复制代码代码如下:

  #include<sys/types.h>

  #include <unistd.h>

  off_t lseek(int fd, off_toffset, intwhence);

  offset用来指定位置,whence参数定义该偏移值的用法。whence可取以下值:

  复制代码代码如下:

  SEEK_SET: The offset is set to offset bytes.

  SEEK_CUR: The offset is set to its current locationplus offset bytes.

  SEEK_END: The offset is set to the size of the fileplus offset bytes.

  成功调用返回从文件头到文件指针被设置处的字节偏移值,失败返回-1。参数offset定义在<sys/types.h>中。

  当偏移量大于文件长度时,出现空洞,空洞不占用存储区。

  read函数

  复制代码代码如下:

  #include <unistd.h>

  ssize_t read(int fd, void*buf, size_tcount);

  将与文件描述符fd关联的文件中读入count个字符放到buf中。返回读入的字节数,它可能小于请求的字节数。如果read调用返回0,就表示未读入任何数据,已到达了文件尾。返回-1,就表示出错。

  write函数

  复制代码代码如下:

  #include <unistd.h>

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

  把缓冲区buf的前count个字节写入与文件描述符fd相关联的文件中。返回实际写入的字节数,通常与count值相同;否则表示出错。出错的一个常见原因是:磁盘已写满,或者超出了一个给定进程的文件长度限制。

  实例:创建一个文件,写入数据,移动当前偏移量,在读数据。

  复制代码代码如下:

  #include<unistd.h>//<unistd.h>必须最早出现,因为它可能会影响到其他头文件。#include<stdio.h>

  #include<fcntl.h>

  #include<string.h>

  #include<errno.h>

  int main()

  {

  char* filename =".//file";

  char buf[100];

  char buf1[5];

  int fd;

  printf("open a file towrite\n");

  if((fd =open(filename,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ))==-1)

  {

  perror("cannot openfile\n");

  return 1;

  }

  printf("open filesuccessfully!\n");

  printf("input astring:");

  gets(buf);

  //write intofile

  if(write(fd,buf,strlen(buf))!=strlen(buf))

  {

  perror("cannot writeintofile\n");

  return 1;

  }

  close(fd);

  printf("open file toread.\n");

  if((fd=open(filename,O_RDONLY))== -1)

  {

  perror("cannot open thefile.\n");

  return 1;

  }

  if(lseek(fd,3,SEEK_SET) ==-1)

  {

  perror("lseekerroe\n");

  return 1;

  }

  //read from the file

  if(read(fd,buf1,4)==-1)

  {

  perror("readerror.\n");

  return 1;

  }

  printf("read from fileis%s\n",buf1);

  close(fd);

  return 0;

  }

  执行与输出结果:

  复制代码代码如下:

  root@jb51:~$gcc -o io io.c

  root@jb51:~$./io

  open a file towrite

  open filesuccessfully!

  input astring:akxivbaslzkncxcasbxbwwvaidxbd

  open file toread.

  read from fileis ivba

  linux 文件I/O教程(2)

  下面介绍了linux中有关文件I/O的相关内容,内核使用三种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。

  一,文件共享

  内核使用三种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。

  1) 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:

  a) 文件描述符标志

  b) 指向一个文件表项的指针

  2) 内核为所有打开文件维持一张文件表。每个文件表项包含:

  a) 文件状态标志(读、写、读写、添些、同步和阻塞等)

  b) 当前文件偏移量

  c) 指向文件v节点表项的指针

  3) 每个打开文件(或设备)都有一个v节点(v-node)结构。v节点包含了文件类型和对比文件进行各种操作的函数的指针。对于大多数文件,v节点还包含了该文件的i节点。i节点包含文件所有者、文件长度、文件所在的设备、指向文件实际数据块在磁盘上所在位置的指针等。

  打开文件的内核数据结构

  如果两个进程各自打开了同一个文件,则如图2所示。假定第一个进程在文件描述符3打开上该文件,而另一个进程在文件描述符4上打开该文件。每个进程都得得到一个文件表项,但对一个给定的文件只有一个v节点表项。每个进程都有自己的文件表项的一个理由是:使每个进程都有自己对该问价的当前偏移量。

  现在对前一节文件I/O(1)的几个操作进一步说明:

  1. 完成write之后,文件中当前偏移量即所增加的字节数。如果当前偏移量大于文件长度,则将i节点中当前文件长度设为当前文件偏移量。

  2. 用O_APPEND打开一个文件,相应标志会被设置到文件状态标识中。每次写时,当前偏移量会被设置为i节点中的文件长度

  3. lseek定位到文件尾端时,则文件当前偏移量会被设置为当前文件长度。

  可能有多个文件描述符指向同一文件表项。调用dup和fork时都能看到这一点。

  多个进程读同一文件能正确工作。但多个进程写同一文件时,可能产生预期不到的后果。可以利用原子操纵避免这种情况。

  原子操作

  一般而言,原子操作指的是由多部组成的操作。如果该院自地执行,要么执行完所以步骤,要么一步也不执行。

  1. 添加至一个文件

  考虑一个进程,它要讲数据添加到一个文件尾端。早期UNIX不支持open,所以可以如下实现:

  复制代码代码如下:

  if(lseek(fd, 0L, 2)<0)

  err_sys(“lseekerror”);

  if(write(fd, buf, 100) !=100)

  err_sys(“writeerror”);

  对于单个进程,这段程序能正常工作。但多个进程就不一定。结社进程A和B都对同一文件进行添加操作。每个进程都打开该文件,此时数据结构之间关系如图2中所示。假定A调用lseek,将A的当前偏移量设置为1500。进程B执行lseek也将其当前偏移量设为1500。然后B调用write,将当前偏移量增至1600。然后内核又进行进程切换使进程A恢复运行,当A调用write时,从其当前偏移量1500处将数据写入,将替换B刚写入到该文件中的数据。

  问题出在逻辑操作“定位到文件尾端处,然后写“使用了两个分开的函数调用。解决办法是使这两个操作成为一个原子操作。O_APPEND标识,使内核每次对文件进行写之前,都将进程当前偏移量设置到该文件的尾端处。

  2.pread和pwrite函数

  原子性地定位搜索和执行I/0。

  复制代码代码如下:

  #include <unistd.h>

  ssize_t pread(int fd, void*buf, size_tcount, off_t offset);

  ssize_t pwrite(int fd,const void *buf,size_t count, off_t offset);

  ssize_t pread(int fd, void*buf, size_tcount, off_t offset);

  ssize_t pwrite(int fd,const void *buf,size_t count, off_t offset);

  dup和dup2函数

  复制代码代码如下:

  #include <unistd.h>

  int dup(int oldfd);

  int dup2(int oldfd, intnewfd);

  上面两个函数都可用来复制一个现存的文件描述符。

  由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。用dup2则可以用newfd参数指定新描述符的数值。如果newfd已经打开,则先将其关闭。如果newfd等于oldfd,则dup2返回newfd而不关闭它。

  图3.3显示了这种情况。

  假定我们的进程执行了:

  newfd = dup(1);

  当此函数执行时,假设下一个可用的描述符是3。因为这两个描述符指向同一个文件表项,所以他们共享文件标志以及同一文件偏移量。

  sync、fsync和fdatasync

  复制代码代码如下:

  #include <unistd.h>

  void sync(void);

  int fsync(int fd);

  int fdatasync(int fd);

  当将数据写入文件时,内核通常将数据复制到一个缓冲区,直到缓冲区写满,再将缓冲区排路输出队列,然后等待其到达队首,才进行实际的I/O操作。这种输出防暑被称为延迟写。延迟写减少了磁盘的读写次数,但却降低了文件内容的跟新速度。当系统发生故障时,延迟写可能造成文件跟新内容的丢失。为了保证磁盘上实际文件系统与缓冲区高速缓存中内容一致性,UNIX系统提供了sync、fsync和fdatasync 三个函数。

  fcntl函数

  复制代码代码如下:

  #include <unistd.h>

  #include <fcntl.h>

  int fcntl(int fd, int cmd,... /* arg */ );

  可以改变已经打开文件的性质。

  复制一个现有的描述符(cmd=F_DUPFD)

  获得或设置文件描述符(cmd=F_GETFD|F_SETFD)

  获得或设置文件状态标志(cmd=F_GETFL|F_SETFL)

  获得或设置异步I/O所有权(cmd=F_GETOWN|F_SETOWN)

  获得或设置记录锁(cmd=F_GETLK|F_SETLK、F_SETLKW)

  可以用fcntl函数设置文件状态,常用设置套接字描述符为非阻塞O_NONBLOCK

  ioctl函数

  #include<sys/ioctl.h>

  int ioctl(int d, intrequest, ...);

  提供了一个用于控制设备及其描述符行为和配置底层服务的接口。

  /dev/fd

  打开文件/dev/fd/n等效于复制描述符n。

Linux入门培训教程 linux系统中文件I/O教程的更多相关文章

  1. Linux系统中文件定位与查找

    Linux系统中文件查找 关键词 文件查找 | find | locate 本文主要介绍有关文件查找的两个命令——find和locate,以及压缩打包的命令——compress, gzip,bzip2 ...

  2. 安装完Linux Mint后,发现系统中竟没有中文输入法

    安装完Linux Mint后,发现系统中竟没有中文输入法,语言支持之后自动更新过程中有些安装包下载失败. 可以采取下面的方法安装上中文输入法. 1. 安装iBus: sudo add-apt-repo ...

  3. 关于Linux系统和Windows系统中文件夹的命名规范

    Windows系统中. 1.在创建文件夹的时候不能以"."开头(但是文件以多个点开头并且还有其他合法字符的话就是合法的) 但是在windows系统中确实见过以一个点".& ...

  4. Xshell6远程访问linux及Xftp6远程针对linux系统中文件操作(附图文详解)

    1.首先我们需要先做好前期准备工作,需要到XManager6官网上将Xshell及Xftp下载并安装,安装过程一直下一步就好了.这里是其官网:http://www.xshellcn.com/.安装完成 ...

  5. linux系统中文件的几种类型

    Linux系统是以文件的形式来进行管理的.Linux文件类型常见的有:普通文件.目录.字符设备文件.块设备文件.符号链接文件等,如果想了解这方面知识的弟兄,就进来了解了解. Linux系统不同于win ...

  6. Linux系统中文件行末尾出现^M的原因及解决办法

    不同系统,有不同的换行符号: 在windows下的文本文件的每一行结尾,都有一个回车('\n')和换行('\r') 在linux下的文本文件的每一行结尾,只有一个回车('\n'); 在Mac下的文本文 ...

  7. linux入门系列17--邮件系统之Postfix和Dovecot

    前文演示了通过Samba和NFS实现文件共享,本篇演示使用Postfix和Dovecot在局域网实现电子邮件收发系统. 电子邮件系统是我们日常生活和工作中非常重要的一个网络服务,在windows下收发 ...

  8. linux入门系列9--用户管理及文件权限控制

    ​ 前面文章分享了Linux下常用命令以及Shell编程相关知识,本节继续学习Linux用户管理及文件权限控制. ​ Linux是多用户多任务操作系统,具有很好的稳定性和安全性.既然是多用户,那就意味 ...

  9. 零基础Linux入门之《Linux就该这么学》

    本书是由全国多名红帽架构师(RHCA)基于最新Linux系统共同编写的高质量Linux技术自学教程,极其适合用于Linux技术入门教程或讲课辅助教材,目前是国内最值得去读的Linux教材,也是最有价值 ...

随机推荐

  1. Java 8中处理集合的优雅姿势——Stream

    在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有这些操作,但是在Java 8之前,集合和数组的处理并不是很便 ...

  2. Spring源码阅读环境搭建

    目录 安装gradle 导入Spring源码 创建测试模块my-test 其他问题 spring-aspects模块构建时报错 本文思维导图 本文将粗略的搭建一个Spring源码的阅读环境,为后面的源 ...

  3. 【Java基础】Java创建对象的五种方式

    Java中创建(实例化)对象的五种方式 1.用new语句直接创建对象,这是最常见的创建对象的方法. 2.通过工厂方法返回对象,如:String str = String.valueOf(23); 3. ...

  4. 交换机安全学习笔记 第五章 DHCP缺陷攻击

    关于DHCP攻击有如下几类攻击方式:   一.耗尽DHCP地址池    通过随机生成源MAC地址,然后伪造DHCPDISCOVER数据包.耗尽DHCP服务器地址池.   免费的攻击工具:  Yersi ...

  5. 【校内test】桶哥的问题

    (以上题目出自_rqy两年前) #A:桶哥的问题——买桶[链接] [题目描述] 桶哥要买一些全家桶.他有a元钱,而每个桶要花b元钱.他能不能买到c个桶? [输入格式] 一行三个整数a, b, c [输 ...

  6. Python数据基础类型-列表

    1,列表的创建 list1 = ['hello', 'world', 1997, 2000] list2 = [1, 2, 3, 4, 5 ] list3 = ["a", &quo ...

  7. jumpserver-1.4.8安装步骤

    1. 组件说明 Jumpserver 为管理后台, 管理员可以通过 Web 页面进行资产管理.用户管理.资产授权等操作, 用户可以通过 Web 页面进行资产登录, 文件管理等操作 koko 为 SSH ...

  8. nginx正常启动,ip直接访问失败问题

    1.查看iptables服务示范启动 service iptables status 2.没有安装的话安装 yum install iptables-services 3.修改配置文件, 添加一行: ...

  9. qt tableview里面添加控件

    在QStyledItemDelegate的paint方法里面 void MyItemModelDeletage::paint(QPainter *painter, const QStyleOption ...

  10. 【Java】 ArrayList和LinkedList实现(简单手写)以及分析它们的区别

    一.手写ArrayList public class ArrayList { private Object[] elementData; //底层数组 private int size; //数组大小 ...