底层文件I/O操作的系统调用

分享到:
           

    文件I/O操作的系统调用,主要用到5个函数:open()、read()、write()、lseek()和close()。这些函数的特点是不带缓存,直接对文件(包括设备)进行读写操作。这些函数虽然不是ANSI C的组成部分,但是是Posix的组成部分。

    基本文件操作

    1.函数说明
    ● open()函数用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
    ● close()函数用于关闭一个被打开的文件。当一个进程终止时,所有被它打开的文件都由内核自动关闭,很多程序都使用这一功能而不显式地关闭一个文件。
    ● read()函数用于将从指定的文件描述符中读出的数据放到缓存区中,并返回实际读入的字节数。若返回0,则表示没有数据可读,即已达到文件尾。读操作从文件的当前指针位置开始。当从终端设备文件中读出数据时,通常一次多读一行。
    ● write()函数用于向打开的文件写数据,写操作从文件的当前指针位置开始,对磁盘文件进行写操作,若磁盘已满或超出该文件的长度,则write()函数返回失败。
    ● lseek()函数用于在指定的文件描述符中将文件指针定位到相应的位置。它只能用在可定位(可随机访问)文件操作中。管道、套接字和大部分字符设备文件是不可定位的,所以在这些文件的操作中无法使用lseek()调用。

    2.函数格式

    open()函数的语法要点如表2.1所示。

表2.1 open()函数语法要点

所需头文件 #include <sys/types.h> /* 提供类型pid_t的定义 */
#include <sys/stat.h>
#include <fcntl.h>
函数原型 int open(const char *pathname, int flags, int perms)
函数传入值 pathname 被打开的文件名(可包括路径名)
flag:文件打开的方式 O_RDONLY:以只读方式打开文件
O_WRONLY:以只写方式打开文件
O_RDWR:以读/写方式打开文件
O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为其设置权限
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。此时open是原子操作,防止多个进程同时创建同一个文件
O_NOCTTY:使用本参数时,若文件为终端,那么该终端不会成为调用open()的那个进程的控制终端
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()函数中,flag参数可通过“|”组合构成,但前3个标志常量(O_RDONLY、O_WRONLY及O_RDWR)不能相互组合。perms是文件的存取权限,既可以用宏定义表示法,也可以用八进制表示法。

    close()函数的语法要点如表2.2所示。

表2.2 close()函数语法要点

所需头文件 #include <unistd.h>
函数原型 int close(int fd)
函数输入值 fd:文件描述符
函数返回值 0:成功
1:出错

    read()函数的语法要点如表2.3所示。

表2.3 read()函数语法要点

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

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

    write()函数的语法要点如表2.4所示。

表2.4 write()函数语法要点

所需头文件 #include <unistd.h>
函数原型 ssize_t write(int fd, void *buf, size_t count)
函数传入值 fd:文件描述符
buf:指定存储器写入数据的缓冲区
count:指定读出的字节数
函数返回值 成功:已写的字节数
1:出错

    在写普通文件时,写操作从文件的当前指针位置开始。

    lseek()函数的语法要点如表2.5所示。

表2.5 lseek()函数语法要点

所需头文件 #include <unistd.h>
#include <sys/types.h>
函数原型 off_t lseek(int fd, off_t offset, int whence)
函数传入值 fd:文件描述符
offset:偏移量,每一读写操作所需要移动的距离,单位是字节,可正可负(向前移,向后移)
whence:
当前位置的基点
SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小
SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小
函数返回值 成功:文件的当前位移
1:出错

    3.函数使用实例

    下面实例中的open()函数带有3个flag参数:O_CREAT、O_TRUNC和O_WRONLY,这样就可以对不同的情况指定相应的处理方法。另外,这里对该文件的权限设置为0600。其源码如下所示:

    下面列出文件基本操作的实例,基本功能是从一个文件(源文件)中读取后10KB数据并复制到另一个文件(目标文件)。在实例中源文件是以只读方式打开的,目标文件是以只写方式打开(可以是读/写方式)的。若目标文件不存在,可以创建并设置权限的初始值为644,即文件所有者可读可写,文件所属组和其他用户只能读。

    读者需要留意的地方是改变每次读/写的缓存大小(实例中为1KB)会怎样影响运行效率。

    /* copy_file.c */
    #include <unistd.h>
    #include <sys/types.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 src_file, dest_file;
        unsigned char buff[BUFFER_SIZE];
        int real_read_len;

        /* 以只读方式打开源文件 */
        src_file = open(SRC_FILE_NAME, O_RDONLY);

        /* 以只写方式打开目标文件,若此文件不存在则创建该文件, 访问权限值为644 */
        dest_file = open(DEST_FILE_NAME, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|
        S_IRGRP|S_IROTH);
        if (src_file < 0 || dest_file < 0)
        {
            printf("Open file error\n");
            exit(1);
        }

        /* 将源文件的读/写指针移到后10KB的起始位置 */
        lseek(src_file, -OFFSET, SEEK_END);

        /* 读取源文件的后10KB数据并写到目标文件中,每次读写1KB */
        while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)
        {
            write(dest_file, buff, real_read_len);
        }
        close(dest_file);
        close(src_file);
        return 0;
    }

    $ ./copy_file
    $ ls -lh dest_file
    -rw-r--r-- 1 david root 10K 14:06 dest_file

    本文选自华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》

   热点链接:

   1、Linux系统调用及用户编程接口(API)
   2、什么是Linux系统调用,包括哪些内容
   3、Linux中的文件及文件描述符
   4、Linux文件系统之虚拟文件系统(VFS)
   5、嵌入式文件系统构建

更多新闻>>