高级IO(1)-UNIX环境高级编程读书笔记

高级IO(一)--UNIX环境高级编程读书笔记

     在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO。

1.非阻塞IO

     可以将系统调用分为两类:低速系统调用和其他。低速系统调用是可能会使进程永远阻塞的一类系统调用。非阻塞IO可以使用不会永远阻塞的IO操作,如果这种操作不能完成,则立即出错返回,表示该操作如果继续执行,将继续阻塞下去。

     对于一个给定的文件描述符,有两种方法对其指定非阻塞的IO:

(1)如果是调用open以获得该描述符,则可指定O_NONBLOCK标志
(2)对于已经打开的一个描述符,则可以调用fcntl打开O_NONBLOCK文件状态标志
下面的函数演示了如何打开一个描述符的文件状态标志以及如何关闭一个文件描述符的文件状态标志:
void set_fl(int fd,int flags)
{
        int val;

        if( (val=fcntl(fd,F_GETFL,0))<0 )
        {
                printf("fcntl F_GETFL error\n");
                exit(0);
        }

        val |= flags;

        if( (fcntl(fd,F_SETFL,val))<0 )
        {
                printf("fcntl F_SETFL error\n");
                exit(0);
        }
}

void clr_fl(int fd,int flags)
{
        int val;

        if( (val=fcntl(fd,F_GETFL,0))<0 )
        {
                printf("fcntl F_GETFL error\n");
                exit(0);
        }

	val &= ~flags;

        if( (fcntl(fd,F_SETFL,0))<0 )
        {
                printf("fcntl F_SETFL error\n");
                exit(0);
        }
}

2.记录锁

     记录在UNIX中是一个误用的概念,因为在UNIX中根本没有“文件记录”这种概念,记录锁的一种个准确的说法是区域锁。这个锁既可以锁一个字节,也可以锁住整个文件。我们使用fcntl函数来操作记录锁,fcntl的函数原型如下:

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int fcntl(int filedes,int cmd,.../*struct flock* flockptr*/);

第一个参数是文件描述符,第二个参数有三种选择如下:

(1)F_GETLK 决定由flockptr描述的锁是否被另外一把锁所排斥。如果有另外一把锁,它阻止创建由flockptr所描述的锁,则把这把锁写入到由flockptr所指向的结构体中。
    如果不存在这种情况,则除了把flockptr所指的结构体中的成员l_type设置为F_UNLCK之外,flockptr所指的结构体中的其他内容保持不变
(2)F_SETLK 设置由flockptr所指向的锁,如果设置失败,则立即出错返回
(3)F_SETLKW 这是F_SETLK的阻塞版本,如果不能建立锁,则会发生阻塞,W代表wait的意思

第三个参数是一个结构体,如下:

           struct flock {
               ...
               short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
               short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* Starting offset for lock */
               off_t l_len;     /* Number of bytes to lock */
               pid_t l_pid;     /* PID of process blocking our lock
                                   (F_GETLK only) */
               ...
           };

l_type表示的是锁的类型,它有3种选择:
(1)F_RDLCK 表示这是一把读锁
(2)F_WRLCK 表示这是一把写锁
(3)F_UNLCK 解锁

l_whence和l_start共同决定了锁的起始位置,l_whence可以为SEEK_SET,SEEK_CUR,SEEK_END,l_start表示相对与l_whence的偏移量。l_len表示加锁区域的长度,如果l_len为0,则表示从指定位置一直加锁到文件结尾。l_pid表示加锁的进程pid。

     关于加锁的过则如下:

                                    读锁                   写锁
                  无锁               可以                   可以
              一把或多把读锁           可以                   拒绝
               一把写锁               拒绝                   拒绝                  

3.锁的隐含继承和释放

     (1)当一个进程结束时,这个进程锁加的锁全部释放

     (2)当一个描述符关闭时,则通过这个描述符锁建立的锁释放

     (3)当关闭一个描述符时,则通过该描述符可以访问的到的任何一把锁都会被释放,这就是说,下面两种情况:

fd1 = open(pathname,...);
read_lock(fd1,...);
fd2 = dup(fd1);
close(fd2);

以及

fd1 = open(pathname,...);
read_lock(fd1,...);
fd2 = open(pathname,...);
close(fd2);

都会将由fd1所建立的锁释放。

     (4)有fork产生的子进程并不继承父进程的锁建立的锁;

     (5)在执行exec后,新程序可以继承原程序的锁