应用程序设计之(2)linux文件I/O编程

之前说了linux的标准I/O这次我们接着说linux的文件I/O编程

在linux系统中,大部分机制都会抽象成一个文件,这样,对它们的操作就像对文件的操作一样。在嵌入式应用开发中,文件I/O编程是最常用的也是最基本的内容,希望友友们好好掌握

一.文件I/O和标准I/O的区别

友友们可能会思考,标准I/O和文件I/O都可以用来访问文件,它们之间有什么区别呢?

        文件I/O又称为低级I/O,遵循POSIX相关标准。任何兼容POSIX标准的操作系统上都支持I/O。标准I/O被称为高级I/O,遵循ANSI C的相关标准。只要开发环境中有标准c库,标准I/O就可以使用。(Linux中使用的时glibc,它是标准c库的超集。不仅有ANSI C中定义的函数,还包括POSIX标准中定义的函数。因此,Linux下既可以使用标准I/O,也可以使用文件I/O)

        通过文件I/O读写文件时,每次操作都会执行相关系统调用,这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销。标准I/O可以看作文件I/O的基础上封装了缓冲机制。先读写缓冲区,必要时候访问实际文件,从而减少了系统调用的次数。

        文件I/O中用文件描述符表示一个打开的文件,可以访问不同类型的文件如普通文件,设备文件和管道文件等。而标准I/O中用FILE(流)表示一个打开的文件,通常只用来访问普通文件。

二.文件I/O操作

        本节主要介绍文件I/O相关函数:open(),close(),read(),write(),lseek()和fcntl()。这些函数的特点是不带缓冲,直接对文件(包括设备)进行读写操作。这些函数不是ANSI C的组成部分,而是由POSIX相关标准来定义。

1.文件的打开和关闭

函数说明open()函数用于创建或打开文件,在打开或创建文件时可以指定文件打开方式及文件的访问权限。

函数格式

open()

 #includ e <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
函数原型
int open(const char *pathname, int flags, mode_t mode)
函数的传入值
pathname: 被打开的文件名(可包括路径名)
flags:文件的打开方式
O_RDONLY:以只读的方式打开文件
O_WRONLY:以只写的方式打开文件
O_RDWR:以读写的方式打开文件
O_CREAT:如果文件不存在,就创建一个新的文件,并用第三个参数为其设置权限
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否已存在
O_NOCTTY:使用本参数时,若打开的是终端文件,那么该终端不会成为当前进程的控制终端
O_TRUNC:若文件已经存在,那么会删除文件中的全部原有数据,并且设置文件大小为0
O_APPEND:以添加方式打开文件,再写文件时,文件读写位置自动指向文件的末尾,即将写入的数据添加到文件的末尾
perms
新建文件的存取权限
可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH)
其中R/W/X分别表示读/写/执行权限
USR/GRP/OTH分别表示文件所有者/文件所属组/其他用户
例如,S_IRUSR|S_IWUSR表示设置文件所有者的可读可写属性。八进制表示法中0600也表示同样的权限
函数返回值
成功 返回文件描述符
失败 -1

关于open的第三个参数单独拿出来讲解一下

首先来看mode_t这个类型,假如系统是32位的,在系统中定义的是一个无符号整型,它表示权限的方法如下
(前面省略16个0)
                            0000 000 000 000 000
从低往上看,每三位为一组,分别表示他人,组,用户,还有特殊权限(SUID权值为4,SGID权值为2,sicky bit权值为1)
比如我们输入一个0664,表示的就是0000 000 110 110 100,等价于-rw-rw-r--

在open()函数中,flags参数可通过“|”组合构成,但前3个标志常量(O_RDONLY,O_WRONLY,以及O_RDWR)不能相互结合。perms是文件的存取权限,既可以用宏定义表示法,也可以用八进制表示法。

close()

#include <unistd.h>
函数原型
int close(int fd);
函数输入值: fd;文件描述符
返回值
成功 0
失败 -1

下面是这两个函数的例子

/*open and close*/
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
    int fd;
    if(fd=open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666)<0)
    {
        perror("fail open");
        return -1;
    }
    close(fd);
    return 0;
}

2.文件读写

1.函数说明

read()函数从文件中读取数据存放到缓存区中,并返回实际读取的字节数。若返回0,则表示没有数据可读,即以及达到文件尾。读操作从文件的当前读写位置开始读取内容,当前读写位置自动往后移。

write()函数将数据写入文件中,并返回实际写入的字节数。写操作从文件的当前读写位置开始写入。对磁盘文件进行写操作时,若磁盘已满,write()函数返回失败。

read()函数的语法

include <unistd.h>
函数原型
ssize_t read(int fd, void *buf, size_t count);
函数的传入值
fd: 文件描述符
buf: 指定存储器读出数据的缓冲区
count: 指定读出的字节数
函数返回值
成功 读到的字节数
0: 已经读到末尾了
-1: 出错

再读普通文件时,若读到要求的字节数之前已到达文件的尾部,则返回的字节数,会小于指定读出的字节数。

write()函数的语法

功能在一个文件描述符上执行写操作
#include <unistd.h>
函数原型
ssize_t write(int fd, const void *buf, size_t count);
函数传入值
fd: 文件描述符
buf:指定存储器写入数据的缓冲区
count: 指定读出的字节数
返回值
成功: 已经写入的字节数
-1: 出出错

下面是这两个函数的例子

/*read and write*/
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define N 64
int main()
{
    int fd,nbyte,sum=0;
    char buf[N];
    if(fd=open("test.txt",O_RDONLY)<0)
    {
        perror("fail to open");
        return -1;
    }
    while(nbyte=read(fd,buf,N)>0)//循环从文件中读取数据,直到读到文件末尾
    {
        sum+=nbyte;//累加每次读取的字节数
    }
    printf("the length of test.txt is %d\n",sum);
    close(fd);
    return 0;
}

3.文件的定位

1.函数说明lseek()函数对于文件当前读写位置进行定位。它只能对可定位文件操作,管道,套接字和大部分字符设备文件不支持此类操作。

2.函数格式

lseek()

 #include <sys/types.h>
 #include <unistd.h>、
函数原型
off_t lseek(int fd, off_t offset, int whence);
函数传入值
fd:文件描述符
offset:相对于基准点whence的偏移量,以字节为单位,正数表示向前移动,负数表示向后移动
whence:当前位置的基点
SEEK_SET :文件的起始位置
SEEK_CUR :文件的当前读写位置
SEEK_END:文件的结束位置
返回值
成功 文件当前读写位置
出错 -1 

例子来一个帮助同学们理解

下面示例中的open()函数带有3个flags参数:O_CREAT,O_TRUNC和O_WRONLY,这样就可以对不同的情况指定相应的处理方法

/*实现的功能是从一个文件(源文件)中读取最后的10kb数据并复制到另一个文件(目标文件)。源文件以只读方式打开,目标文件以只写方式打开,若目标文件不存在,可以创建并设置权限的初始值为644,即文件所有者可读可写,文件所属组和其他用户只能读*/
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFFER_SIZE 1024 //每次读写缓存的大小,影响运行效率
#define SRC_FILE_NAME "src_file"  //源文件名
#define DEST_FILE_NAME "dest_file" //目标文件名
#define OFFSET 10240 //复制的数据大小
int main()
{
    int fds,fdd;
    unsigned char buff[BUFFER_SIZE];
    int read_len;
    //以只读方式打开源文件
    if(fds=open(SRC_FILE_NAME,O_RDONLY)<0)
    {
        perror("fail to open src_file");
        return -1;
    }
    //以只写方式打开目标文件。若文件不存在则创建该文件,访问权限值为644
    if(fdd=open(DEST_FILE_NAME,O_WRONLY|O_CREAT|O_TRUNC,0644)<0)
    {
        perror("fail to open dest_file");
        return -1;
    }
    //将源文件的读写指针移到最后10kb的起始位置
    lseek(fds,-OFFSET,SEEK_END);
    //读取源文件的最后10kb数据并写到目标文件中,每次读写1kb
    while((read_len=read(fds,buff,sizeof(buff)))>0)
    {
        write(fdd,buf,read_len);            

    }
    close(fds);
    close(fdd);
        return 0;
}

4.文件锁

(1).fcntll()函数说明

前面的函数实现了文件的打开,读写等基本操作。这一节将讨论的是在文件已经共享的情况下如何操作,也就是多个程序共同操作一个文件的情况。Linux中通常采用的方法是给文件上锁,来解决共享资源的竞争。

文件锁包括建议性锁和强制锁。建议性锁要求每个相关程序在访问文件之前检查是否有锁存在,并尊重已经有的锁,一般情况下,不建议使用建议性锁,因为无法保证每个程序都有自动检测是否有锁的功能。而强制性锁是由内核执行的锁,当1一个文件被上锁,进行写入操作的时候,内核将阻止其他任何程序对该有文件进行读写操作。采用强制性锁对性能的影响较大,每次读写操作内核都检测是否有锁的存在。

        在Linux系统中,实现文件上锁的函数有lockf()和fcntl(),其中lockf()用于对文件施加加建议性锁,而fcntl()不仅可以施加建议性锁还可以施行强制性锁。同时,fcntl()还能对文件的某一记录上锁,也就是记录性锁。

        记录锁又可分为读取锁和写入锁,其中读取锁又被称为共享锁,多个同时执行的程序允许在文件的同一部分建立读取锁。而写入锁又被称为排斥锁,在任何时刻只能有一个程序在文件的某个部分上建立写入锁。显然,在文件的同一部分不能同时建立读取锁和写入锁。

(2).函数格式

fcntl()

 #include <unistd.h>
 #include <fcntl.h>
函数原型
int fcntl(int fd, int cmd, ... /* arg */ );
函数传入值
fd: 文件描述符
cmd:
F_GETLK: 检测文件锁状态
F_SETLK: 设置lock的描述的文件锁
F_SETLKW: 这是F_SETLK的阻塞版本w表示wait等待
函数返回值
成功 0
出错 -1

如果cmd和锁操作相关,则第三个参数的类型为struct * flock

定义如下

struct flock
{
    ...
    short 1_typer;
    off_t 1_start;
    short 1_whence;
    off_t len;
    pid_t 1_pid;
    ....
}

flock结构体中每个成员的取值含义如下

1_type
F_RDLCK: 读取锁(共享锁)
F_WRLCK: 写入锁(排斥锁)
F_UNLCK: 解锁
1_start
加锁区域在文件中的相对位移量(字节),与l_whence值一起决定加锁的区域的起始位置
1_whence:相对位移量的起点(同lseek的whence)
l_len: 加锁区域的长度
l_pid: 具有阻塞当前进程的锁,其持有进程的进程号存放在l_pid中,仅由F_GETLF返回

若要加锁整个文件,可以将l_start设置为0,l_whence设置为SEEK_SET,l_len设置为0

(3). fcntl()使用例子

下面给出了使用fcntl()函数对文件加记录锁的代码。首先给flock结构体赋予相应的值,接着调用两次fcntl()函数,

第一次用F_GETLK命令判断是否可以执行flock结构所描述的锁操作:若成员l_type的值为F_UNLCK,表示文件当前可执行相应的锁操作;否则成员l_type的值表示当前已有的锁类型,并且成员l_pid被设置为拥有当前文件锁的进程号。

第二次用F_SETLK或F_SETLKW命令设置flock结构所描述的锁操作,后者是前者的阻塞版本。使用后者时,当不能执行相应的上锁/解锁操作时,程序会被阻塞,直到可以操作为止。

兄弟们,今天锤了两个板块,手实在是不行了,希望大家可以学到真正的知识。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞赴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值