1、當多個進程同時寫一個文件時,有可能出現數據混亂,這個問題需要解決。解決方案:進程間的同步或文件鎖。
2、文件鎖就是當一個進程讀寫文件時,對其他進程進行讀寫的限制。
3、文件鎖的結論:1)一個進程讀,允許其他進程讀,但不允許其他進程寫。
2)一個進程寫,其他進程既不能讀也不能寫。
4、文件鎖是一個讀寫鎖,包括讀鎖和寫鎖。
讀鎖是一個共享鎖,允許其他進程讀(共享),但不允許其他進程寫(鎖)。如果進程在讀文件,就應該上讀鎖。
寫鎖是一個互斥鎖,不允許其他進程讀和寫(互斥)。如果是寫文件,就應該上寫鎖。
5、函數fcntl(fd, cmd, ...)當cmd爲F_SETLK/F_SETLKW時,可以對文件進行上鎖。
6、當使用文件鎖時,第三個參數是一個結構體類型指針,結構體原型如下:
struct flock {
short l_type; /* 鎖的類型: F_RDLCK(讀鎖),F_WRLCK(寫鎖), F_UNLCK(解鎖) */
short l_whence; /* 鎖的起始點的參考位置:SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 針對參考位置的偏移量(整數值) */
off_t l_len; /* 鎖的區間長度 */
pid_t l_pid; /* 上鎖的進程號,只對F_GETLK有效,一般只給-1 */
};
7、進程結束自動釋放文件鎖,但是最好還是程序員自己釋放。
8、文件鎖只是內存中的一個標識,不會真正鎖定文件。fcntl()不能鎖定write函數,只能鎖定其他進程的加鎖行爲。
9、文件鎖的正確用法是:在調用read()函數之前用fcntl()加讀鎖,能加再讀,讀完以後再釋放鎖;在調用write()函數之前用fcntl()加寫鎖,能加再寫,寫完之後在釋放鎖。但不管怎麼加鎖,類似vi的編輯器是無法鎖定。
10、當F_SERLK加不上鎖時,直接返回-1;而F_SETLKW加不上鎖時,會繼續等待,等到能加上鎖爲止。
11、F_GETLK不是獲得當前鎖,而是測試一下某個鎖能不能加上,並不是真正的加鎖。
實例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(){
int fd = open("a.txt",O_RDWR | O_CREAT | O_TRUNC, 0666);
if(fd == -1){
perror("open"),exit(-1);
}
//定義鎖
struct flock lock;
lock.l_type = F_WRLCK; //寫鎖
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 10;
lock.l_pid = -1;
//int res = fcntl(fd, F_SETLK, &lock);
int res = fcntl(fd, F_SETLKW, &lock);
if(res != -1){
int res = write(fd,"he",2);
if(res == -1){
perror("write1"),exit(-1);
}
sleep(5);
res = write(fd,"llo",3);
if(res == -1){
perror("write2"),exit(-1);
}
lock.l_type = F_UNLCK;
res = fcntl(fd, F_SETLK, &lock);
if(res == -1){
printf("釋放鎖失敗!\n");
}
else{
printf("文件鎖被釋放!\n");
}
}
else{
printf("文件被鎖定,無法讀寫!\n");
}
close(fd);
return 0;
}
1)傳入型參數 給函數傳值。比如int add(int, int);
2)傳出型參數 帶回函數結果,一般是指針類型。比如void add(int *sum){
int i = 0;
*sum = 0;
for(i = 1; i < 10; i++){
*sum += i;
}
}
3)傳入傳出型參數 先傳入一個值,再帶出一個值。比如void add(int, int, int *sum);其中sum就是傳入傳出型參數。
函數的返回值,可以用return直接返回,也可以使用傳出型參數返回。
13、access()可以判斷當前用戶對文件的權限和文件是否存在。函數原型int access(char* fname, int mode);
參數:
fname代表文件名
mode代表文件模式,取值可爲:
F_OK 文件是否存在
R_OK 是否具有讀權限
W_OK 是否具有寫權限
X_OK 是否具有可執行權限
成功返回0,失敗返回-1。
實例:
/*
文件權限函數access演示
*/
#include <stdio.h>
#include <unistd.h>
int main(){
if(access("a.txt",R_OK) == 0){
printf("可讀!\n");
}
if(access("a.txt",W_OK) == 0){
printf("可寫!\n");
}
if(!access("a.txt",X_OK)){
printf("可執行!\n");
}
if(!access("a.txt",F_OK)){ //在讀文件之前,檢測文件是否存在
printf("文件存在!\n");
}
return 0;
}
chmod 修改文件權限,比如chmod("a.txt",0666);
truncate/ftruncate 指定文件的大小,比如truncate("a.txt",100);
remove 刪除文件或空目錄
rename 修改文件名
umask 修改新建文件時,系統默認的權限屏蔽字。系統默認屏蔽其他用戶的寫權限,-0002。
函數原型mode_t umask(mode_t)傳入新的權限,返回之前的權限屏蔽字,用於處理之後的恢復。
mmap 可以映射物理內存,但也可以映射文件,默認情況下映射文件,映射物理內存需要加MAP_ANONYMOUS標識。
實例:
/*
文件函數演示
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
chmod("a.txt",0666);
truncate("a.txt",100);
int fd1 = open("aa",O_RDWR | O_CREAT,0666);
if(fd1 == -1){
perror("open1"),exit(-1);
}
mode_t old = umask(0022);
int fd2 = open("bb",O_RDWR | O_CREAT,0666);
if(fd2 == -1){
perror("open2"),exit(-1);
}
umask(old);
int fd3 = open("cc",O_RDWR | O_CREAT,0666);
if(fd3 == -1){
perror("open3"),exit(-1);
}
close(fd1);
close(fd2);
close(fd3);
return 0;
}
目錄相關函數:
mkdir() 創建一個目錄
rmdir() 刪除一個空目錄
chdir() 切換當前目錄
getcwd() 取當前目錄(返回絕對路徑形式)
讀取目錄函數
opendir() 打開一個目錄,返回目錄流
readdir() 讀目錄的一個子項(子目錄/子文件)
效果相當於命令:ls 目錄
closedir() 關閉目錄流(不寫也可以)
實例:
/*
目錄函數演示
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
void printall(const char* path){ //遞歸函數
DIR* dir = opendir(path);
if(dir){ //目錄是一個子項
struct dirent* dirent = NULL;
while(dirent = readdir(dir)){
if(strcmp(".",dirent -> d_name) == 0 || strcmp("..",dirent -> d_name) == 0){ //跳過目錄“.”和“..”,避免死循環
continue;
}
if(dirent -> d_type == 4){ //目錄
printf("[%s]\n",dirent -> d_name);
char buf[100] = {};
sprintf(buf,"%s/%s",path, dirent -> d_name);
printall(buf);
}
else{ //文件
printf("%s\n",dirent -> d_name);
}
}
}
else{
return;
}
}
int main(){
printall("../");
return 0;
}
知識補充:
使用遞歸的條件:
1、使用遞歸後,問題簡化而不是複雜化。
2、遞歸必須有退出條件。
3、使用遞歸要注意效率問題。