文件锁与I/O复用

当多个用户共同使用、操作一个文件的情况,这时,Linux 通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。在 Linux 中,实现文件上锁的函数有lock和fcntl,其中flock用于对文件施加建议性锁,而fcntl不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl还能对文件的某一记录进行上锁,也就是记录锁。
记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分上建立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。

 

以验证写入锁又称为排斥锁为例:

 1 /*fcntl.c*/
 2 #include <unistd.h>
 3 #include <sys/file.h>
 4 #include <sys/types.h>
 5 #include <sys/stat.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 void lock_set(int fd, int type);
 9 /*lock_set函数*/
10 void lock_set(int fd, int type)
11 {
12     struct flock lock;
13     lock.l_whence = SEEK_SET;//赋值lock结构体
14     lock.l_start = 0;
15     lock.l_len =0;
16     while(1)
17     {
18         lock.l_type = type;
19         /*根据不同的type值给文件上锁或解锁*/
20         if((fcntl(fd, F_SETLK, &lock)) == 0)
21         {
22             if( lock.l_type == F_RDLCK )
23                 printf("read lock set by %d\n",getpid());
24             else if( lock.l_type == F_WRLCK )
25                 printf("write lock set by %d\n",getpid());
26             else if( lock.l_type == F_UNLCK )
27                 printf("release lock by %d\n",getpid());
28             return;
29         }
30         /*判断文件是否可以上锁*/
31         fcntl(fd, F_GETLK,&lock);
32         /*判断文件不能上锁的原因*/
33         if(lock.l_type != F_UNLCK)
34         {
35             /*该文件已有写入锁*/
36             if( lock.l_type == F_RDLCK )
37                 printf("read lock already set by %d\n",lock.l_pid);
38             /*该文件已有读取锁*/
39             else if( lock.l_type == F_WRLCK )
40                 printf("write lock already set by %d\n",lock.l_pid);
41             getchar();
42         }
43     }
44 }
45 void main(void)
46 {
47     int fd;
48     fd=open("hello",O_RDWR | O_CREAT, 0666);
49     if(fd < 0)
50     {
51         perror("open ERROR");
52         exit(1);
53     }
54     /*给文件上写入锁*/
55     lock_set(fd, F_WRLCK);
56     getchar();
57     /*给文件接锁*/
58     lock_set(fd, F_UNLCK);
59     getchar();
60     close(fd);
61     exit(0);
62 }

同时运行2个终端可以观察到:

 

前面的fcntl函数解决了文件的共享问题,接下来该处理I/O 复用的情况了。总的来说,I/O 处理的模型有5 种。
· 阻塞 I/O模型:在这种模型下,若所调用的I/O 函数没有完成相关的功能就会使进程挂起,直到相关数据到才会出错返回。如常见对管道设备、终端设备和网络设备进行读写时经常会出现这种情况。
· 非阻塞模型:在这种模型下,当请求的I/O 操作不能完成时,则不让进程睡眠,而且返回一个错误。非阻塞I/O 使用户可以调用不会永远阻塞的I/O 操作,如open、write和read。如果该操作不能完成,则会立即出错返回,且表示该I/O 如果该操作继续执行就会阻塞。
· I/O多路转接模型:在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O,而是让其中的一个函数等待,在这期间,I/O 还能进行其他操作。如本节要介绍的select函数和poll函数,就是属于这种模型。
· 信号驱动 I/O 模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时可以启动一个I/O 操作决定的。
· 异步 I/O模型:在这种模型下,当一个描述符已准备好,可以启动I/O 时,进程会通知内核。现在,并不是所有的系统都支持这种模型。

struct timeval {
  long tv_sec; /* second */
  long tv_unsec; /* and microseconds*/
}

该例中主要实现将文件hello1 里的内容读出,并将此内容每隔10s 写入hello2 中去。在这里建立了两个描述符集,其中一个描述符集inset1 是用于读取文件内容,另一个描述符
集inset2是用于写入文件的:

 1 /*select.c*/
 2 #include <fcntl.h>
 3 #include <stdio.h>
 4 #include <unistd.h>
 5 #include <stdlib.h>
 6 #include <time.h>
 7 void main(void)
 8 {
 9     int fds[2];
10     char buf[7];
11     int i,rc,maxfd;
12     fd_set inset1,inset2;
13     struct timeval tv;
14     tv.tv_sec=2;
15     tv.tv_usec=0;
16     /*首先按一定的权限打开hello1文件*/
17     if((fds[0] = open ("hello1", O_RDWR|O_CREAT,0666))<0)
18         perror("open hello1 ERROR");
19     /*再按一定的权限打开hello2文件*/
20     if((fds[1] = open ("hello2", O_RDWR|O_CREAT,0666))<0)
21         perror("open hello2 ERROR");
22     if((rc = write(fds[0],"Hello!\n",7)))
23         printf("rc=%d\n",rc);
24     lseek(fds[0],0,SEEK_SET);
25     /*取出两个文件描述符中的较大者*/
26     maxfd = fds[0]>fds[1] ? fds[0] : fds[1];
27     /*初始化读集合inset1,并在读集合中加入相应的描述集*/
28     FD_ZERO(&inset1);
29     FD_SET(fds[0],&inset1);
30     /*初始化写集合inset2,并在写集合中加入相应的描述集*/
31     FD_ZERO(&inset2);
32     FD_SET(fds[1],&inset2);
33     /*循环测试该文件描述符是否准备就绪,并调用select函数对相关文件描述符做对应操作*/
34     while(FD_ISSET(fds[0],&inset1)||FD_ISSET(fds[1],&inset2))
35     {
36         if(select(maxfd+1,&inset1,&inset2,NULL,&tv)<0)
37             perror("select ERROR");
38         else
39         {
40             if(FD_ISSET(fds[0],&inset1))
41             {
42                 rc = read(fds[0],buf,7);
43                 if(rc>0)
44                 {
45                     buf[rc]='\0';
46                     printf("read: %s\n",buf);
47                 }
48                 else
49                     perror("read ERROR");
50             }
51             if(FD_ISSET(fds[1],&inset2))
52             {
53                 rc = write(fds[1],buf,7);
54                 if(rc>0)
55                 {
56                     buf[rc]='\0';
57                     printf("rc=%d,write: %s\n",rc,buf);
58                 }
59                 else
60                     perror("write ERROR");
61             sleep(10);
62             }
63         }
64     }
65     exit(0);
66 }

 

posted @ 2015-02-28 10:17  ht-beyond  阅读(165)  评论(0编辑  收藏  举报