UNIX环境高级编程(第3章 文件I/O)

    函数可简单分类为API函数与系统调用函数。

API函数包含:

1)标准库函数,可跨平台移植,如fopenfwritefreadfseekfclose,在头文件<stdio.h>中定义。

2)系统API函数(即特定平台的API函数),不能跨平台移植,如windows平台的CreateFileWriteFileReadFileDeleteFileCloseHandle,在头文件<windows.h>中定义。Unix平台的openwritereadlseekclose,在头文件<fcntl.h><unistd.h>中定义。

    系统调用是操作系统向应用程序提供的访问内核的入口点。

   

    本章主要讲解的是Unix系统文件读写操作的系统API。主要文件I/O函数有:openreadwritelseek以及closeUNIX系统中的大多数文件I/O只需用到这5个函数。这些函数被称为不带缓冲的I/O,其中不带缓冲指的是每个readwrite都调用内核中的一个系统调用。

    上面所述的大多数文件,包括的类型有普通文件、目录文件、块设备、字符设备、命名管道、套接字、符号链接等。


1 文件描述符

    对于内核而言,所有打开的文件都通过文件描述符引用。

    当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。

    当读或写一个文件时,使用opencreat返回的文件描述符标识该文件,将其作为参数传送给readwrite

    文件描述符是一个非负整数,其变化范围是0~OPEN_MAX(允许每个进程最多打开的个数).按照惯例,UNIX系统shell使用文件描述符0(对应符号常量STDIN_FILENO)与进程的标准输入相关联,1(STDOUT_FILENO)与标准输出相关联,2(STDERR_FILENO)与标准出错输出相关联。


2 文件IO函数

1)open函数

头文件

#include <fcntl.h>

函数原型

int open(const char *pathname, int oflag, …/*mode_t mode*/)

参数

pathname:打开或创建的文件名

oflag:文件访问模式(文件状态标志

三选一:O_RDONLYO_WRONLYO_RDWR

可选:O_APPENDO_CREATO_EXCLO_TRUNCO_NOCTTYO_NONBLOCK

可选:O_DSYNCO_SYNCO_RSYNC

mode:文件访问权限(该参数可选)

用户读、写、执行权限:S_IRUSRS_IWUSRS_IXUSR

组读、写、执行权限:S_IRGRPS_IWGRPS_IXGRP

其他读、写、执行权限:S_IROTHS_IWOTHS_IXOTH

   仅当创建新文件时才使用该参数,用于指定文件的访问权限

返回

成功返回文件描述符;失败返回-1并设置全局变量errno

功能

打开或创建一个文件

注意:

    由open返回的文件描述符一定是最小的未用描述符数值,这点可被用来在标准输入、标准输出或标准出错输出上打开新的文件

头文件

#include <fcntl.h>

函数原型

int creat(const char *pathname, mode_t mode)

参数

pathname:待创建的文件名

mode:文件访问权限

返回

成功返回文件描述符;失败返回-1并设置全局变量errno

功能

以只写方式打开所创建的文件,等价于:

open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode)

2)close函数

头文件

#include <unistd.h>

函数原型

int close(int fildes);

参数

fildes:文件描述符

返回

成功返回0;出错返回-1

功能

关闭一个打开的文件,即终止文件描述符fildes与其对应文件之间的关联。

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

3)lseek函数

头文件

#include <unistd.h>

#include <sys/types.h>

函数原型

off_t lseek(int fildes, off_t offset, int whence);

参数

fildes:文件描述符

offset:指定位置(offset可正可负,但不能使新的文件偏移量的值为负)

whence:定义偏移值的用法

SEEK_SET:将文件偏移量设置为文件开始加offset个字节

SEEK_CUR:将文件偏移量设置为其当前值加offset个字节

SEEK_END:将文件偏移量设置为文件末尾加offset个字节

返回

成功返回新的文件偏移量;失败返回-1并设置全局变量errno

功能

对文件描述符fildes的读写指针进行设置,即显式地为一个打开的文件设置偏移量

注意:

    通常文件的当前偏移量应当是一个非负整数,但是,某些设备也可能允许负的偏移量。但对于普通文件,则其偏移量必须是非负值。

        lseek返回-1的情况:文件描述符引用的是一个管道,FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE

4)write函数

头文件

#include <unistd.h>

函数原型

ssize_t write(int fildes, const void *buf, size_t nbytes);

参数

fildes:文件描述符

buf:缓冲区

nbyte:字节数

返回

成功返回已写的字节数,出错则返回-1

其返回值通常与nbytes的值相同,否则表示出错。

功能

向打开的文件写数据,即把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。

5)read函数

头文件

#include <unistd.h>

函数原型

ssize_t read(int fildes, void *buf, size_t nbytes);

参数

fildes:文件描述符

buf:缓冲区

bytes:字节数

返回

成功返回读到的字节数,若已到文件结尾则返回0,若出错则返回-1

功能

从打开的文件中读数据,即从与文件描述符fildes相关联的文件里读入nbytes个字节的数据,并把它们放到数据区buf中。


3 文件共享

        UNIX系统支持在不同进程间共享打开的文件。内核使用三种数据结构表示打开的文件,进程表项、文件表项、v节点结


进程表项:

每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述符表。每

个描述符各占一项,包含a)文件描述符标志(close_on_exec)b)指向一个文件表项的指针。

文件表项:

内核为所有打开文件维持一张文件表,每个文件表项包含:a)文件状态标志,b)

前文件偏移量,c)指向该文件v节点表项的指针

V节点表:

每个打开文件(或设备)都有一个v节点结构。V节点包含了文件类型和对此文件

进行各种操作的函数的指针。


打开该文件的每个进程都得到一个文件表项,但对一个给定的文件只有一个v节点表项。每个进程都有自己的文件表项的一个理由是:使每个进程都有它自己的对该文件的当前偏移量。

1)文件描述符标志fd flags

如上图,每个文件描述符fd0fd1等,各自都包含了文件描述符标志文件表项指针。其中的文件描述符标志file descriptors flagsclose_on_exec)用于判断子进程在调用exec函数时是否需要关闭该标志对应的文件描述符。子进程调用exec函数时,加载执行另一个程序,此时子进程完全被新程序代替,并从新程序的main函数开始执行。

当文件描述符对于的文件描述符标志被设置时,执行exec时,该描述符被关闭;否则该描述符将始终处于被打开的状态。

当前只定义了一个文件描述符标志FDCLOEXEC
      0
exec时不关闭已经打开的文件描述符
 
      1
exec时关闭已经打开的文件描述符

/*通过fcntl函数设置文件描述符标志*/
int flags;    
flags = fcntl(fd, F_GETFD, 0);       /*先获取fd对应的文件描述符标志值,接着根据需要修改再设置。若直接执行F_SETFD命令进行设置时,会关闭以前设置的标志位*/
fcntl(fd, F_SETFD, flags|FD_CLOEXEC);/*修改fd的文件描述符标志值,并设置*/

2)文件状态标志:File status flags

文件状态标志

说明

O_RDONLY

O_WRONLY

O_RDWR

O_APPEND

O_NONBLOCK

O_SYNC

O_DSYNC

O_RSYNC

O_FSYNC

O_ASYNC

只读打开

只写打开

为读、写打开

每次写时追加

非阻塞模式

等待写完成(数据和属性)

等待写完成(仅数据)

同步读、写

等待写完成

异步I/O


4 其他重要函数

1)dup函数,dup2函数

头文件

#include <unistd.h>

函数原型

int dup(int filedes);

参数

filedes:现存的文件描述符,即一个已经打开的文件的文件描述符

返回

成功返回新的文件描述符,若出错则返回-1dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。

功能

复制一个现存的文件描述符,返回的新的描述符与参数filedes共享同一个文件表项。

    dup函数常用于输出的重定向。示例摘自http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=359433&page=1#pid2385375

#include <stdio.h>;
#include <unistd.h>;
#include <stdlib.h>;
#include <fcntl.h>;
#include <sys/types.h>;
#include <sys/stat.h>;
#include <string.h>;
#include <strings.h>;

int main()
{
        int sfd = dup(STDOUT_FILENO), testfd;

        printf("sfd = [%d]\n", sfd);

        testfd = open("./temp",O_CREAT | O_RDWR | O_APPEND);
        if (-1 == testfd)
        {
                printf("open file error.\n");
                exit(1);
        }

        /* 重定向 */
        if (-1 == dup2(testfd,STDOUT_FILENO) ) {
                printf("can't redirect fd error\n");
                exit(1);
        }

        /* 此时向stdout写入应该输出到文件 */
        write(STDOUT_FILENO,"file\n",5);

        /* 恢复stdout */
        if (-1 != dup2(sfd,STDOUT_FILENO) ) {
                printf("recover fd ok \n");

                /* 恢复后,写入stdout应该向屏幕输出 */
                write(STDOUT_FILENO,"stdout\n",7);
        }

        printf("gogogogogogo!\n");
        close(testfd);
}

头文件

#include <unistd.h>

函数原型

int dup2(int filedes, int filedes2);

参数

filedes:现存的文件描述符,即一个已经打开的文件的文件描述符

filedes2:指定新描述符的数值,如果filedes2已经打开,则先将其关闭。如若filedes等于filedes2,则dup2返回filedes2,而不关闭它。

返回

成功返回指定的新的文件描述符,若出错则返回-1

功能

复制一个现存的文件描述符,返回的指定的新描述符与参数filedes共享同一个文件表项。

    fd = open("/dev/null", O_RDWR, 0);
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    if(fd > 2)
        close(fd);

        上述代码段经常用于守护进程中,使得守护进程重启时,0,1,2不会对应在标准输入、标准输出、标准错误输出上。

实现与dup2功能相同的函数:
int mydup2(int fd1, int fd2)
{
    int fd;
    int i = 0;
    int fd_array[OPEN_MAX];

    /*参数错误处理*/
    if (fd1 < 0 || fd1 > OPEN_MAX || fd2 < 0 || fd2 > OPEN_MAX)
    {
        printf("The fd is not in the limits[%d~%d], fd1=%d, fd2=%d\n", 0, 
                OPEN_MAX, fd1, fd2);
        return -1;
    }
    
    /*判断fd1是否为有效文件描述符*/
    if ((fd = dup(fd1)) == -1)
    {
        printf("fd1 is the bad file destriptor, fd1=%d\n", fd1);
        return -1;
    }
    else
    {
        close(fd);
    }

    
    /*判断fd1是否为有效文件描述符*/
    if ((fd = dup(fd2)) == -1)
    {
        /*do nothing*/
    }
    else /*fd2为有效文件描述符*/
    {
        close(fd);
        if (fd1 == fd2)
        {
            return fd2;/*fd2与fd1相等则直接返回fd2,且不关闭fd2*/
        }
        close(fd2);
    }
    
    /*利用dup生成与fd2相等的文件描述符*/
    while ((fd = dup(fd1)) != fd2)
    {
        fd_array[i++] = fd;
    }
    
    for (i = i - 1; i >= 0; i--)
    {
        close(fd_array[i]);
    }

    return fd2;
}

2)fcntl函数

头文件

#include <fcntl.h>

函数原型

int fcntl(int filedes, int cmd, …/*int arg*/)

参数

第一个参数filedes:文件描述符

第二个参数cmd:

第三个参数:用于获得/设置记录锁时,该参数是指向一个结构的指针,其他情况下都是一个整数。

返回

若成功则依赖于cmd,若出错则返回-1

功能

fcntl函数有5中功能:

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

2)      获得/设置文件描述符标记(cmd=F_GETFDF_SETFD)

3)      获得/设置文件状态标记(cmd=F_GETFLF_SETFL)

4)      获得/设置异步IO所有权(cmd=F_GETOWNF_SETOWN)

5)      获得/设置记录锁(cmd=F_GETLKF_SETLKF_SETLKW)

3)ioctl函数

头文件

#include <unistd.h>   /*System V*/

#include <sys/ioctl.h>  /*BSD and Linux*/

#include <stropts.h>   /*XSI STREAM*/

函数原型

int ioctl(int filedes, int request, …)

参数

filedes:文件描述符

request:请求

返回

若出错则返回-1,若成功则返回其他值

功能

ioctl函数是I/O操作的杂物箱,不能用本章中其他函数表示的I/O操作通常都能用ioctl表示。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值