Unix C (六)

文件鎖:
    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;
}


    12、C語言中,參數可以有三種:
        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;
}


    14、其他函數:
        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、使用遞歸要注意效率問題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章