我們先假設有一個檔案叫Resource.TXT,它會被兩個程序 (Process 0, Process 1)去存取。可是,如果當這兩個行程,同時要寫資料到Resource.TXT這個檔案,那麼一定會有問題,會發生Race Condition (競走現象)。為了要解決這樣的問題,我們可以先用一個很笨的方法:
1. 當Process 0要去存取Resource.TXT時,先建立一個檔案鎖 (Lock File);
2. 而Process 1要去存取Resource.TXT時,先去判斷檔案鎖是否存在,如果存在,就表示有其它的行程正在存取當中,所以程序先行等待;
3. 當Process 0存取結束時,就把檔案鎖給刪除;
4. 而Process 1再去確認檔案鎖是否存在,若不存在,表示沒有行程在存取,換Process 1自行存取。
很簡單吧!其實,這種避免許多行程去存取某一個資源的競走現象,在作業系統的觀念中還蠻重要的;在更深入的Linux Kernel Space也會用到。
所以,基於以上4點,用幾行簡單的程式碼,具體實踐。
#include "proc.h"
#define SECONDS 30
int main(void)
{
FILE *fp = (FILE *)NULL;
char *pStr0 = (char *)NULL;
creat(LOCK_FILE, S_IROTH);
fp = fopen(RESOURCE_FILE, "r");
if (fp == (FILE *)NULL) {
fprintf(stderr, "Open the Resource File: %s ERROR!! \n", RESOURCE_FILE);
exit(EXIT_FAILURE);
}
pStr0 = (char *)calloc(sizeof(char), 16);
fread((void *)pStr0, sizeof(char), 16, fp);
fprintf(stdout, "pStr0: %s", pStr0);
fprintf(stdout, "Waiting for %d seconds.... \n", SECONDS);
sleep(SECONDS);
fprintf(stdout, "Done!! \n");
free((void *)pStr0);
fclose(fp);
remove(LOCK_FILE);
return 0;
}
首先,我們在Process 0程式一開始執行時,就先用creat()函式來建立一個檔案鎖 (/var/tmp/Resource.lck),接著,我們開始用fopen(), fread(), fprintf()等函式來讀取Resource.TXT檔案,並且將之顯示出來;
再來,為了讓Process 1有時間去存取和驗證,用sleep()函式讓程序等待30秒之後再結束,
最後,在程序結束前,要記得把檔案鎖給刪除。
再看Process 1的程式碼:
#include "proc.h"
#define SECONDS 3
int main(void)
{
struct stat stTemp;
int fd = 1, nRet = 1;
FILE *fp = (FILE *)NULL;
char *pStr1 = (char *)NULL;
bzero((void *)&stTemp, sizeof(struct stat));
do {
fd = open(LOCK_FILE, O_RDONLY);
nRet = fstat(fd, &stTemp);
fprintf(stdout, "Warning: The file was LOCKed!! Please wait.... \n");
sleep(SECONDS);
} while (nRet == 0);
close(fd);
pStr1 = (char *)malloc(sizeof(char) * 16);
fp = fopen(RESOURCE_FILE, "r");
if (fp == (FILE *)NULL){
fprintf(stderr, "Open the Resource File: %s ERROR!! \n", RESOURCE_FILE);
exit(EXIT_FAILURE);
}
pStr1 = (char *)malloc(sizeof(char) * 16);
fread((void *)pStr1, sizeof(char), 16, fp);
fprintf(stdout, "pStr1: %s", pStr1);
fclose(fp);
free((void *)pStr1);
return 0;
}
程序一開始就先判斷檔案鎖是否存在,這裡判斷的方法是,用低階I/O的open()函式開啟檔案之後,用fstat()函式去判斷檔案鎖是否存在;因為fstat()函式執行成功會回傳0,所以在while-loop當中,fstat()一直回傳0就表示檔案鎖存在,我們就顯示一小段警告訊息,並且用sleep()函式等待3秒鐘,直到檔案鎖被Process 0給移除之後,才會跳出迴圈。
跳出回圈之後,就可以讀取Resource.TXT了,把內容讀出來。
再把其它的相關檔案程式碼補齊:
proc.h:
#ifndef __PROC_H__ #define __PROC_H__ #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #define RESOURCE_FILE "./Resource.TXT" #define LOCK_FILE "/var/tmp/Resource.lck" #endifMakefile:
GCC := gcc CFLAGS := -o OBJS := proc0.o proc1.o EXEC := proc0.out proc1.out RM := rm -rf .PHONY: all all: $(EXEC) $(EXEC): $(OBJS) @-$(GCC) $(CFLAGS) proc0.out proc0.o @-$(GCC) $(CFLAGS) proc1.out proc1.o $(OBJS): %.o: %.c @-$(GCC) -c $< $(CFLAGS) $@ .PHONY: clean cleanall install print tar dist TAGS check test clean: @-$(RM) *.o cleanall: clean @-$(RM) $(EXEC) install: print: tar: dist: TAGS: check: test:Resource.TXT:
Hello!! World!!執行結果如下圖所示:
這樣的作法,相當的笨拙,且不夠聰明。好在Linux的User Space下還提供了另外一種更好的方式─fcntl()函式 (File Control);這一篇文章只不過是個引言,接下就會記錄如何使用fcntl()函式來避免競走現象。
沒有留言:
張貼留言