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、使用递归要注意效率问题。


你可能感兴趣的:(linux,程序员,unix,C语言,文件)