linux低級檔案編程中也講到
多使用者多任務作業系統中非常重要的一個內容就是檔案鎖。使用者在更新檔案時,期望可以使用某種機制,防止兩種進程同時更新檔案同一地區而造成丟失,或者防止檔案內容在未更新完畢時被讀取等並發引起的問題,這種機制就是檔案鎖。
進程在操作檔案期間,可以使用檔案鎖,鎖定檔案中的敏感部分,防止其他進程越權操作該部分資料。函數fcntl提供了對檔案任意地區置鎖的能力,既可以鎖住全部檔案,又可以鎖住檔案的部分記錄,故檔案鎖又成為"記錄鎖".
根據檔案鎖的訪問方式,可以區分讀鎖和寫鎖兩種。讀鎖又名共用鎖定,它用來防止進程讀取的檔案記錄被更改。檔案記錄可以同時設定多個讀鎖,但當有一個讀鎖存在時,就不能在該記錄出設定寫鎖。
寫鎖又名互斥鎖,它用來保證檔案更改記錄時不被幹擾,確保檔案一致性和完整性,防止寫丟失或讀"髒"資料。檔案記錄一旦設定了寫鎖,就不能再設定任意鎖,除非寫鎖接觸。
檔案記錄在同一時刻,可以設定多個讀鎖,單僅能設定一個寫鎖,並且讀、寫不能不能同時存在。
當函數fntl專用於鎖操作時,其原型為:
int fcntl(int fildes,int cmd,struct flock *arg);
其中,結構flock用於描述檔案鎖的資訊,定義於"fcntl.h"中,如下表示:
1. struct flock
2. {
3. short l_type;/*鎖類型,取值為F_RDLCK、F_WRLCK、或F_UNLCK之一,分別代表申請讀鎖、申請寫鎖和釋放鎖*/
4. short l_whence;/*鎖地區開始地址的相對位置,類似於lseek中whence參數,取值是SEEK_SET SEEK_CUR SEEK_END之一,分別表示相對檔案起始位置、檔案當前位置、檔案結束位置*/
5. long l_start;/*鎖地區開始地址位移量,同l_whence共同確定鎖地區*/
6. long l_len;/*鎖的長度,0表示鎖至檔案末*/
7. long l_pid;/*擁有鎖的進程ID號*/
8. };
當函數fcntl專用於鎖時,參數cmd的三種取值:
F_GETLK
F_SETLK
F_SETLKW
{檔案鎖最典型的應用於兩個方面:一個鎖定檔案中的臨界資料,比如並發投票時檔案記錄的投票數;二是利用具有互斥性質的寫鎖,實現進程的並發控制}
2)檔案鎖操作
在鎖機制的使用中,最常見的操作有鎖的請求、釋放和測試等,這些操作代買基本類似
以下函數都是作者自己做的封裝:
(1)測試鎖
設計函數SeeLock,它查詢檔案描述符fd對應檔案的鎖資訊,其原型為:
void SeeLock(int fd,int start,int len);
函數查詢描述符fd對應檔案從位移start出開始的len位元組中的鎖資訊
1. /*----測試所源碼----lock1.c----*/
2. void SeekLock(int fd,int start,int len)
3. {
4. struct flock arg;
5. arg.l_type=F_WRLCK;
6. arg.l_whence=SEEK_SET;
7. arg.l_start=start;
8. arg.l_len=len;
9. if(fcntl(fd,F_GETLK,&arg)==-1)
10. fprintf(stderr,"See Lock failed./n");
11. else if(arg.l_type==F_UNLCK)
12. fprintf(stderr,"NO LOCK FROM %d TO %d,n",start, len);
13. else if(arg.l_type==F_WRLCK)
14. fprintf(stderr,"WRITE LOCK FROM %d TO %d ,id=%d/n",start,len,arg.l_pid);
15. else if(arg.l_type==F_RDLCK)
16. fprintf(stderr,"READ LOCK FROM %d To %d,id=%d/n",start,len,arg.l_pid);
17. }
(2)申請讀鎖
共用鎖定申請函數GetReadLock,原型為:
void GetReadLock(int fd,int start,int len);
以阻塞模式在檔案描述符fd相應的檔案中申請共用讀鎖,鎖定的地區從位移start處開始的len位元組
1. /*---阻塞申請共用讀鎖源碼---lockl.c*/
2. void GetReadLock(int fd,int start,int len)
3. {
4. struct flock arg;
5. arg.l_type=F_RDLCK;
6. arg.l_whence=SEEK_SET;
7. arg.l_start=start;
8. arg.l_len=len;
9. if(fcntl(fd,F_SETLKW,&arg)==-1)
10. fprintf(stderr,"[%d] Set Read Lock failed./n",getpid());
11. else
12. fprintf(stderr,"[%d] Set Read Lock From %d To %d",getpid(),start,len);
13. }
(3)申請寫鎖
void GetWriteLock(int fd,int start,int len);
1. /*---阻塞申請共用寫鎖源碼---lockl.c*/
2. void GetReadLock(int fd,int start,int len)
3. {
4. struct flock arg;
5. arg.l_type=F_WRLCK;
6. arg.l_whence=SEEK_SET;
7. arg.l_start=start;
8. arg.l_len=len;
9. if(fcntl(fd,F_SETLKW,&arg)==-1)
10. fprintf(stderr,"[%d] Set Write Lock failed./n",getpid());
11. else
12. fprintf(stderr,"[%d] Set Write Lock From %d To %d",getpid(),start,len);
13. }
(4)釋放鎖
設計檔案鎖釋放函數ReleaseLock,原型為:
void ReleaseLock(int fd,int start,int len);
1. /*---釋放鎖源碼---lockl.c*/
2. void GetReadLock(int fd,int start,int len)
3. {
4. struct flock arg;
5. arg.l_type=F_UNLCK;
6. arg.l_whence=SEEK_SET;
7. arg.l_start=start;
8. arg.l_len=len;
9. if(fcntl(fd,F_SETLKW,&arg)==-1)
10. fprintf(stderr,"[%d] UnLock failed./n",getpid());
11. else
12. fprintf(stderr,"[%d] UnLock From %d To %d",getpid(),start,len);
13. }
以下是一個檔案鎖控制進程的執行個體,如下:
1. #include<stdio.h>
2. #include<fcntl.h>
3. void main()
4. {
5. int fd;
6. struct flock arg;
7. if((fd=open("/tmp/tlockl",O_RDWR|O_CREAT,0755))<0)
8. {
9. fprintf(stderr,"open file failed./n");
10. retrun;
11. }
12. SeeLock(fd,0,10);
13. GetReadLock(fd,0,10);/*申請讀鎖*/
14. SeeLock(fd,11,20);
15. GetWriteLock(fd,11,20);/*申請寫鎖*/
16. sleep(30);
17. ReleaseLock(fd,0,10);
18. ReleaseLock(fd,11,20);
19. }
作者“pstary”