linux 系统编程 文件IO

Fileio

1.open() 系统调用

头文件

  #include <sys/types.h>

  #include <sys/stat.h>

  #include <fcntl.h>

原型

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

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

  flags O_RDONLY O_WRONLY O_RDWR

  

  O_APPEND每次写操作都写入文件的末尾

  O_CREAT如果指定文件不存在,则创建这个文件

  O_EXCL如果要创建的文件已存在,则返回-1,并且修改errno的值

  O_TRUNC如果文件存在,并且允许写,则清空文件全部内容(即将其长度截短为0)

  O_NOCTTY如果路径名指向终端设备,不要把这个设备用作控制终端。

  O_NONBLOCK如果路径名指向FIFO/块文件/字符文件,则把文件的打开和后继I/O设置为非阻塞模式

以下三个常量同样是选用的,它们用于同步输入输出

  O_DSYNC等待物理I/O结束后再write。在不影响读取新写入的数据的前提下,不等待文件属性更新。

O_RSYNCread等待所有写入同一区域的写操作完成后再进行

O_SYNC等待物理I/O结束后再write,包括更新文件属性的I/O

注意:

O_CREAT 选择后一定要填写mode参数

Mode
参数S_I + R+W+X + U

U表示设置所有者 单独设置一项填USR

G表示设置组用户 单独设置一项填GRP

O表示设置其他人 单独设置一项填OTH

Mode
还可以设置为八进制数值如 0644

2.creat()

头文件

#include <sys/types.h>

     #include
<sys/stat.h>

  #include <fcntl.h>

原型

Int creat(const char* name,mode_t mode);

相当于

Open(name ,O_RDONLY|O_CREAT|O_TRUNC, mode)

3.Read()

头文件

#include<unistd.h>

函数原型

Ssize_t
read(int fd ,void buf,size_t len);

Read 是否会阻塞由所读的设备决定,在open等函数中设定

Size_t 最大值为SIZE_MAX

Ssize_t最大值为 SSIZE_MAX

4.Write()

头文件

#include<unistd.h>

原型

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

Read和write实际都是和缓冲区交互数据,不是直接和磁盘交换数据

详解https://www.cnblogs.com/JohnABC/p/5821660.html

5.同步IO

Fsync() 和fdatasync()

头文件

#include<unistd.h>

Int fsync(int fd);

直到fd的数据写入磁盘缓冲之后才返回
,同时还会建立时间戳和inode信息等元数据

Int fdatasync(int fd);

直到fd的数据写入磁盘缓冲之后才返回

可能导致的问题是文件数据更新,但是目录没有更新,那么数据会成功写入,但是目录项没有更新,文件无法访问。

解决方法:目录也要调用fsync();

Sync()

Void sync(void);

同步所以缓存。

同步标志 O_SYNC

在open()的时候,加上这个标志,所有在文件上的IO都会同步

O_DSYNC  
只同步普通数据

O_RSYNC

直接IO

使用0_DIRECT open(),系统将最小化IO管理,直接将用户缓冲区和设备进行初始化,所有的IO都是同步的,操作在完成之前不会返回。

4.关闭文件close()

头文件

#include<unistd.h>

Int close(int fd);

解除fd和文件的关联文件描述符不在有效

同一个程序绑定的fd如果close了,然后在打开,之前的fd和后面的指相同,是可以用来执行文件操作的

5.Lseek()

头文件

#include<sys/types.h>

#include<unistd.h>

函数原型

Off_t
lseek(int fd,off_t pos,int origin);

Origin 可以设置为

SEEK_CUR 从文件当前位置移动 pos位

SEEK_END        从文件尾开始移动pos位

SEK_SET            从文件开始移动pos位

返回新的文件位置

Ret = -1 表示出错

定位读写

Pread()

头文件

#include<unistd.h>

也就是说:

#define _XOPEN_SOURCE

是为了可以使用 5. The X/Open
Portability Guide 的功能。

函数原型

Ssize_t
pread(int fd,void *buf,size_t count,off_t pos);

Pwrite

函数原型

Ssize_t
pwrite(int fd,const void * buf,size_t count,off_t pos);

在pos处进行读写

不会改变fd的位置,和read write 混合用会导致覆盖

文件截断

头文件

#include<unistd>

#include<sys/types.h>

函数原型:

Int
ftruncate(int fd,off_t len);

Int
truncate(char * path,off_t len);

IO 多路复用

6.Select

头文件

#include<sys/time.h>

#include<sys/types.h>

#include<unistd.h>

函数原型:

Int
select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval
*timeout);

n
所有集合中文件描述符最大值加一

timeout
结构体

#include<sys/time.h>

Struct{

Long
tv_sec;//seconds

Long
tv_usec; //microseconds

};

Select返回时这个结构体状态未定义,最好重新初始化,较新版本会把他初始化为剩余时间

如果时间为0,select立即返回,报告文件描述符不可用,不在等待后续操作。

管理文件描述符宏定义

FD_CLR(int
fd,fd_set *set);

FD_ISSET(int
fd,fd_set *set); //测试一个文件描述符是否在集合内

FD_SET(int fd,fd_set *set); //添加一个文件描述符到指定集合

FD_ZERO(fd_set *set);  //移除指定集合中所有描述符 ,每次调用select()前调用

文件描述符集合是静态建立的,最大值有限制,为 FD_SETSIZE; linux 为1024

通过将select 三个集合设置为0 ,将超时设置为非空可以实现sleep;

7.Pselect

posix版本的select

头文件

#include<sys/select.h>

#define _XOPEN_SOURCE 600

原型

Int
pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,

Const struct timespec *timeout,

Const sigset_t * sigmask);

和select比区别

超时参数类型改变,pselect 使用timespec精度为秒和纳秒,比秒和毫秒精度高,事实是都在秒之后不可靠

Pselect 不修改timeout参数

Select没有sigmask参数,pselect添加是为了解决信号和文件描述符之间的竞争条件

一般还是选择使用select

注意:

Select在有文件描述符状态变化时返回,返回时修改了文件描述符集合,返回后的集合中只有状态改变的fd,每次使用时要重新赋值。

8.Poll

System v 的I/O多路复用

头文件:

#include<sys/poll.h>

函数原型:

Int poll(struct pollfd *fds,unsigned int
nfds, int timout);

Pollfd
结构

头文件

#include<sys/poll.h>

Struct
pollfd

{
    int fd;  //文件描述符

Short event;// 监视的文件描述符上的事件位掩码

Short revent;// 发生在文件描述上的事件位掩码

}


常量


说明


POLLIN


普通或优先级带数据可读


POLLRDNORM


普通数据可读


POLLRDBAND


优先级带数据可读


POLLPRI


高优先级数据可读


POLLOUT


普通数据可写


POLLWRNORM


普通数据可写


POLLWRBAND


优先级带数据可写


POLLERR


发生错误


POLLHUP


发生挂起


POLLNVAL


描述字不是一个打开的文件

注意:

Poll在返回后只修改了fds的revents字段,所以可以反复使用。

与select区别。

9.Ppoll

和pselect 类似

标准I/O

打开文件

10.fopen()

头文件

#include<stdio.h>

函数原型:

FILE*
fopen(const char* path,const char *mode);

文件打开后关联到一个新的流

打开方式有: r r+ w w+ a a+

成功返回指针,失败返回NULL。

11.fdopen

头文件

#include<stdio.h>

函数原型:

FILE* fdopen(int
fd,const char *mode);

讲一个打开的文件的描述符转化为流,转化时mode要一致

12.fclose

头文件

#include<stdio.h>

函数原型:

Int
fclose(FILE * stream);

成功返回0 并将缓冲中没写入数据写入,失败返回EOF

13. fcloseall

#include <stdio.h>

#define _GUN_SOURCE

Int fcloseall();

14.fgetc()

#include<stdio.h>

Int fgetc(FILE*stream);

函数结果必须以int保存
,EOF会被返回,输出时要检查
c== EOF

15.ungetc()

#include<stdio.h>

Int ungetc(int c,FILE*stream);

成功返回0,失败返回EOF;

16.fgets

#include<stdio.h>

Char * fgets(char *str,int size,FILE*
stream);

读取size-1 个字节到str,成功返回str失败返回NULL。

读到EOF或者换行符的时候结束,如果读到换行符就写入str。

最大输入长度为LINE_MAX 在limits.h中定义

17.fread

18.fputc()

#include<stdio.h>

Int fputc(int c,FILE *stream);

19fputs()

#include<stdio.h>

Int fputs(const char *str,FILE *stream);

20. fwrite

21.fseek()

#include<stdio.h>

Int fseek(FILE *stream,long offset,int whence);

Whence有如下值:

SEEK_CUR 文件位置为 当前加offset

SEEK_END文件位置为文件尾加offset

SEEK_SET文件位置为0+offset

错误返回-1;正确返回0

22.fsetpos()

#include<stdio.h>

Int fsetpos(FILE * stream,fpos_t *pos);

23.rewind

#include<stdio.h>

Void rewind(FILE* stream);将流置于初始位置

24 ftell()

#include<stdio.h>

long ftell(FILE* stream);

返回文件的当前流位置。错误返回-1.

25fgetpos

#include<stdio.h>

int fgetpos(FILE* stream,fpos_t *pos);

26.fflush

#include<stdio.h>

Int fflush(FILE* stream);  将流中数据写回文件

成功返回0;

Int ferror 
检查是否有错

Int feof     检查是否文件尾

Void clearer 清空失败和文件尾标志,不可恢复

27.fileno

#include<stdio.h>

Int fileno (FILE *stream);

返回文件描述符失败返回-1;

28.Setvbuf

#include<stdio.h>

Int setvbuf(FILE * stream, char * buf, int
mode, size_t size);

_IONBF

_IOLBF

_IOFBF

函数在除了无缓存模式下,buf可以指向一个size大小的缓冲区,如果buf为空则glic分配

Stdio.h中的BUFSIZE定义默认的缓冲区大小

29.readv 和writev

#include<sys/uio.h>

Ssize_t readv(int fd ,const struct iovec
*iov,int count);

Ssize_t writev(int fd ,const struct iovec
*iov,int count);

从iov描述的缓冲区读或者写count个segment的数据到fd。

Struct iovec 描述的缓冲区成为segment

#include<sys/uio.h>

Struct iovec

{

Void *iov_base;

Size_t iov_len;

}

30.epoll

#include<sys/epoll.h>

Int epoll_create(int size);

返回与这个实例关联的文件描述符  size为要监测的文件描述符数目。出错返回-1

EINVAL 
size不是正数

ENFILE      系统打开文件数达到上限

ENOMEN 没有足够内存

返回的文件描述符需要用close关闭。

#include<sys/epoll.h>

Int epoll_ctl(int epfd,int op,int fd,struct
epoll_event *event);

#include<sys/epoll.h>

Struct epoll_event{

_u32 events;

Union{

Void *ptr;

Int fd;

_u32 u32;

_64 u64;

} data;};

Epoll_ctl 调用会关联epoll实例和epfd, op指定要进行的操作,event描述更具体的行为

Op的取值

EPOOL_CTL_ADD   把fd加到监测列表

EPOOL_CTL_DEL      删除fd到监测列表

EPOOL_CTL_MOD   使用event修改监测列表中的fd

Event取值:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:表示对应的文件描述符可以写;

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR:表示对应的文件描述符发生错误;

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要使用epoll_ctl_mod修改fd

#include<sys/epoll.h>

Int epoll_wait ( int epfd,struct
epoll_event * events,int maxevents,int timeout );

Timeout  毫秒

返回成功
events指向epoll_event结构体,其中最多有maxevents 返回值为事件个数,出错返回-1

边缘触发和水平触发

Epoll_ctl中events设置为 RPOLLE时,设置边缘触发。

例子:

  1. 生产者向管道写入数据1KB
  2. 消费者管道调用epoll_wait等待数据。

在边缘触发中,直到1数据写完epoll_wait才返回,水平触发则epoll_wait立即返回

水平触发为默认形式,select和poll都是水平模式。水平触发只关心状态,边缘触发关系事件。

内存映射

31.Mmap

#include<sys/mman.h>

void*
mmap(void* addr,size_t len,int prot,int flags,int fd,off_t offset);

addr 参数告诉内核映射文件的最佳地址,只是提示不是强制。一般设置为 0;调用返回内存映射的开始地址。

Prot 设置访问权限 PROTNONE 无法访问,一般这样设置没有意义。

PROT_READ     可读

PROT_WRITE   可写

PROT_EXEC      可执行

访问的权限不能与打开文件的访问模式冲突。

Flag参数描述了映射的类型和一些行为

MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。

MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。

MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。

MAP_DENYWRITE //这个标志被忽略。

MAP_EXECUTABLE //同上

MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。

MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。

MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。

MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。

MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。

MAP_FILE //兼容标志,被忽略。

MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。

MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。

MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。

off_toffset:被映射对象内容的起点。

成功返回映射区域的地址

SIGBUS   //进程访问无效的映射去死产生

SIGSEGV 
//进程试图在只读映射区写入数据时产生。

32.Sysconf

#include<unistd.h>

Long sysconf(int name);

返回name属性的值失败返回-1.

例子:
long pagesize = sysconf(SC_PAGR_SIZE);获取页面大小

#include<unistd.h>

Int getpagesize(void);

#include<asm/pages.h>

Int page_size = PAGE_SIZE;

33.munmap

#include<sys/mman.h>

int munmap(void*
addr,size_t len);

移除从addr开始的len字节长度的映射。移除后访问会产生SIGSEGV信号

成功返回0,失败返回-1;

34.mremap()

#include<sys/mman.h>

#include<unistd.h>

#define _GNU_SOURCE

Void * mremap(void *addr,size_t
old_size,size_t new_size,unsigned long flags);

修改文件大小size 从old到new

35.mprotect

#include<sys/mman.h>

Int mprotect(const void *addr,size_t
len,int prot);

修改映射区域权限

在有些系统中mprotect只能修改mmap分配的区域,在linux可以修改所有的

36.msync

#include<sys/mman.h>

Int msync(void *addr,size_t len,int flags);

Flag 取值

MS_ASYNC 同步操作一部发生,系统调度更新,mysnc立即返回。

MS_INVALIDATE 所有该块映射的拷贝失效,未来对该映射的操作直接写到硬盘

MS_SYNC
所有同步同步进行,在write执行完毕后msync才返回。

#include<sys/mman.h>

Int Madvise(void *addr,size_t len,int len);

#include<fcntl.h>

Int posix_fadvise(int fd ,odd_t
offset,off_t len,int advise);

#include<fcntl.h>

Ssize_t readahead(int fd ,off64_t
offset,size_t count);

同步Synchronized ,同步Synchronous 异步asynchronous

同步在读写操作执行完之后才返回。

异步不用读写操作执行完就可以返回

Synchronized保证事件发生,属于更深层次,NoSynchronized写操作加入队列就返回,只确保到内核缓存区域。

时间: 2024-10-01 04:38:15

linux 系统编程 文件IO的相关文章

Linux系统编程-文件IO函数

一.ioctl 函数 ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据.也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据.例如,在串口线上收发数据通过read/write操作,而串口的波特率.校验位.停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置. #in

Linux系统编程---文件I/O思想

1.文件如何在Linux中存储 Linux中任何事物都可以用一个文件来表示,或者通过特殊的文件提供. 一个文件由目录项.inode和数据块组成. 硬盘的最小存储单位叫做"扇区"(Sector),每个扇区储存512字节(相当于0.5KB). 操作系统读取硬盘的时候,不会一个扇区一个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block).这种由多个扇区组成的"块",是文件存取的最小单位."块"的

Linux系统编程--文件描述符的复制dup()和dup2()【转】

本文转载自:http://blog.csdn.net/tennysonsky/article/details/45870459 dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件. 这个过程类似于现实生活中的配钥匙,钥匙相当于文件描述符,锁相当于文件,本来一个钥匙开一把锁,相当于,一个文件描述符对应一个文件,现在,我们去配钥匙,通过旧的钥匙复制了一把新的钥匙,这样的话,旧的钥匙和新的钥匙都能开启这把锁.对比于 d

Linux系统编程——文件描述符的复制:dup()和dup2()

dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件. 这个过程类似于现实生活中的配钥匙,钥匙相当于文件描述符,锁相当于文件,本来一个钥匙开一把锁,相当于,一个文件描述符对应一个文件,现在,我们去配钥匙,通过旧的钥匙复制了一把新的钥匙,这样的话,旧的钥匙和新的钥匙都能开启这把锁.对比于 dup(), dup2() 也一样,通过原来的文件描述符复制出一个新的文件描述符,这样的话,原来的文件描述符和新的文件描述符都指向

Linux系统编程-文件打开关闭

一.文件描述符 对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的.当打开或者创建一个文件的时候,内核向进程返回一个文件描述符(非负整数).后续对文件的操作只需通过该文件描述符,内核记录有关这个打开文件的信息(file结构体). 一个进程启动时,默认打开了3个文件,标准输入.标准输出.标准错误,对应文件描述符是0(STDIN_FILENO).1(STDOUT_FILENO).2(STDERR_FILENO),这些常量定义在unistd.h头文件中. 另外介绍下面两个函数: fil

4412开发板Linux系统编程实战-字符设备控制

在 linux 驱动中字符驱动是必须掌握的,本章主要介绍字符设备应用的程序,无论是学习了后面的知识自己写的字符驱动,还是已有的字符驱动,都需要能够写一些简单的应用程序. 即使从事 linux 驱动的工作,linux 驱动写出来之后,也需要由驱动程序员编写简单的应用进行测试的. 另外,关于驱动部分,迅为电子有专门的驱动实验教程提供给大家学习,大家有了这些基础之后再去学习底层的知识就会很容易了. 在使用手册的第八章,大家可以看到这些 c 程序也是可以在 Android 下面运行的,只不过没有图形界面

Linux学习记录--文件IO操作相关系统编程

文件IO操作相关系统编程 这里主要说两套IO操作接口,分别是: POSIX标准 read|write接口,函数定义在#include<unistd.h> ISO C标准 fread|fwrite接口,函数定义在#include<stdio.h> 有书上说POSIX标准与ISO C标准的区别在于文件读写是否带缓冲区,我则不是很认同,因此POSIX标准下的IO操作也是带缓冲区的,至于这两个标准下的IO性能谁更加好则不一定,因为这和缓冲区的大小,以及用户逻辑有很大关系. POSIX标准

嵌入式 Linux系统编程(一)——文件IO

嵌入式 Linux系统编程(一)--文件IO 一.文件IO概念 linux文件IO操作有两套大类的操作方式:不带缓存的文件IO操作,带缓存的文件IO操作.不带缓存的属于直接调用系统调用(system call)的方式,高效完成文件输入输出.它以文件标识符(整型)作为文件唯一性的判断依据.这种操作不是ASCI标准的,与系统有关,移植有一定的问题.而带缓存的是在不带缓存的基础之上封装了一层,维护了一个输入输出缓冲区,使之能跨OS,成为ASCI标准,称为标准IO库.不带缓存的方式频繁进行用户态 和内核

嵌入式 Linux系统编程(二)——文件描述符控制函数fcntl

嵌入式 Linux系统编程(二)--文件描述符控制函数fcntl 由于fcntl函数实在过于灵活和复杂,本文将fcntl函数从文件IO中单独列出来,便于详细解读.函数原型如下: #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); fcntl函数用于控制操作文件描述符fd,对文件描述符的控制操作由cmd控制命令来控制,arg参数为可选参数,是否需要arg参数取决于控制命令