Linux-C-day-5-读写锁

读写锁

当一个进程正在读或者修改某个文件的某个部分时,组织其他进程修改同一个文件或者同一个文件的某个区域。对于Linux来说一切皆是文件,包括IO设备共享内存;

接口

int fcntl(int fd, int cmd, struct flock *lock);表示锁操作;fd:表示文件描述符;cmd:表示命令,F_GETLK:表示获取锁;设置锁:F_SETLK,用于加解锁;F_SETLKW用于设置加解阻塞锁;

struct flock {
          i_type  //F_RDLCK:表示读取锁,读取锁是共享锁;F_WRLCK:是写入锁,写入锁是排它锁;F_UNLCK:表示用于解锁;
          I_whence //SEEK_SET:表示在文件的开头为锁定的起始位置;SEEK_CUR:表示以目前读写位置为锁定的起始位置;SEEK_END:表示以文件的结尾为锁定的起始位置;
          I_start  //表示相对于I_whence位置的偏移量;
          I_len    //表示锁定区域的长度,0表示全文;
          I_pid    //当前占用锁的的PID,只对F_GETLK命令有效;

};

 返回值:-1表示失败,0表示SET命令设置成功;
 记录锁:获取锁信息fcntl(fd,F_GETLK);设置锁:fcntl(fd,F_SETLK,arg);fcntl(fd,F_SETLKW,arg);
file_wrlock.c

#include 
#include 
#include 
#include 
#include 
int fd;

void handler(int sig){
    struct flock lock;
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; 
    if(-1 == fcntl(fd,F_SETLKW,&lock)){
        perror("fcntl error");
        exit(1);
    }
}

int main(int argc, char* argv[]){
    signal(SIGUSR1,handler);
    int c,start = 0,len = 0;
    while((c = getopt(argc,argv,"s:l:"))!=-1){
        switch(c){
        case 's':
            start = atoi(optarg);
            break;
        case 'l':
            len = atoi(optarg);
            break;
        }
    }

    if(optind != argc -1){
        printf("usage:%s [-s ] [-l ] \n",argv[0]);
        return 1;
    }
    fd = open(argv[optind],O_WRONLY);
    if(-1 == fd){
        perror("open error");
        return 1;
    }
    struct flock lock;
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = start;
    lock.l_len = len; 
    if(-1 == fcntl(fd,F_SETLKW,&lock)){
        perror("fcntl error");
        return 1;
    }
    pause();
    for(;;);
    close(fd);
}

文件读锁:
file_rdlock.c

#include 
#include 

int main(int argc, char* argv[]){
    if(2!=argc){
        printf("usage:%s \n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDONLY);
    if(-1 == fd){
        perror("open error");
        return 1;
    }
    struct flock lock;
    lock.l_type = F_RDLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; 
    if(-1 == fcntl(fd,F_SETLK,&lock)){
        perror("fcntl error");
        return 1;
    }
    pause();
    close(fd);
}

view_lock.c:用于查看锁的状态,但是一般只能用于查看写锁的状态,不能够查看读锁的状态;

#include 
#include 
#include 

int main(int argc, char* argv[]){
    if(2!=argc){
        printf("usage:%s \n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDWR);
    if(-1 == fd){
        perror("open error");
        return 1;
    }
    struct flock lock;
    bzero(&lock,sizeof(lock));
    if(-1 == fcntl(fd,F_GETLK,&lock)){
        perror("fcntl error");
        return 1;
    }
    printf("file:%s,lock type:%d,start:%d,len:%d,by %d\n",argv[1],lock.l_type,lock.l_start,lock.l_len,lock.l_pid);

}

file_unlock.c:用于解除锁,解锁操作只能在本进程完成,否则解锁操作就会失败;

#include 
#include 

int main(int argc, char* argv[]){
    if(2!=argc){
        printf("usage:%s \n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_WRONLY);
    if(-1 == fd){
        perror("open error");
        return 1;
    }
    struct flock lock;
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; 
    if(-1 == fcntl(fd,F_SETLKW,&lock)){
        perror("fcntl error");
        return 1;
    }
    pause();
    close(fd);
}
读写锁的分类

 访问操作:同一个进程访问只会覆盖已有的锁;锁包含写入锁(排它锁);读写锁(共享锁);加锁区域:文件锁,对于整个文件上锁;记录锁:对于文件的部分区域上锁;
锁的实现方式:
 建议性锁(Advisory locking)别名, 劝诫锁,协作锁,每个上锁的进程都需要检查是否有锁存在,当然还需要尊重已有的锁,这个规范需要程序员来实现;
 强制性锁(Mandatory locking):当文件悲伤所来进行写入操作时,在锁定该文件的进程释放锁之前,内核会阻止任何对该文件的读或者写的访问(open、read、write),每次读或者写访问之前,都得检查锁是否存在,但是缺点是内核实现,系统开销很大,并且系统之间的兼容性很差(Mac OS不支持强制性锁);本质是内核读写文件自动处理;
设置:mount -o remount,mand/nomand分别表示允许强制锁定,禁止强制锁定;写该文件权限:chmod g+s,g-x来设置权限;
进程死锁的情况:
deadlock.c

#include 
#include 
#include 
#include 
void lock(const char* pathname){

    int fd = open(pathname,O_WRONLY);
    if(-1 == fd){
        perror("open error");
        exit(1);
    }
    struct flock lock;
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; 
    if(-1 == fcntl(fd,F_SETLKW,&lock)){
        perror("fcntl error");
        exit(1);
    }
    //close(fd);
}
int main(int argc, char* argv[]){
    if(3!=argc){
        printf("usage:%s  \n",argv[0]);
        return 1;
    }
    printf("PID:%d lock file %s\n",getpid(),argv[1]);
    lock(argv[1]);
    printf("sleep 1s\n");
    sleep(1);
    printf("PID:%d lock file %s\n",getpid(),argv[2]);
    lock(argv[2]);
    pause();
}

补充一个关于lseek函数
lseek_append.c:用于实现在文件的特定位置插入数据,但是不进行覆盖操作;

#include 
#include 
#include 
#include 
#include 

int main(int argc,char* argv[]){
    if(4 != argc){
        printf("usage:%s   \n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDWR);
    if(-1 == fd){
        return 1;
    }
    off_t off = lseek(fd,atoi(argv[2]),SEEK_SET);
    if(-1 == off){
        return 1;
    }
    struct stat stat_buf;
    fstat(fd,&stat_buf);
    size_t save_size = stat_buf.st_size - off;
    //char buf[save_size];
    char *buf = malloc(save_size);
    if(-1 == read(fd,buf,save_size)){
        return 1;
    }
    
    off = lseek(fd,atoi(argv[2]),SEEK_SET);
    if(-1 == off){
        return 1;
    }
    if(-1 == write(fd,argv[3],strlen(argv[3]))){
        return 1;
    }
    if(-1 == write(fd,buf,save_size)){
        return 1;
    }

    free(buf);
    buf = NULL;
}

lseek_write.c:用于执行在特定的位置写入数据的功能

#include 
#include 
#include 
#include 
#include 

int main(int argc,char* argv[]){
    if(4 != argc){
        printf("usage:%s   \n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_WRONLY);
    if(-1 == fd){
        return 1;
    }
    off_t off = lseek(fd,atoi(argv[2]),SEEK_SET);
    if(-1 == off){
        return 1;
    }
    if(-1 == write(fd,argv[3],strlen(argv[3]))){
        return 1;
    }
}

lseek_read.c:用于在特定的位置进行数据的读取功能

#include 
#include 
#include 
#include 
#include 

int main(int argc,char* argv[]){
    if(4 != argc){
        printf("usage:%s   \n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDONLY);
    if(-1 == fd){
        return 1;
    }
    off_t off = lseek(fd,atoi(argv[2]),SEEK_SET);
    if(-1 == off){
        return 1;
    }
    size_t len = atoi(argv[3]);
    char buf[len+1];
    bzero(buf,len+1);
    if(-1 == read(fd,buf,len)){
        return 1;
    }
    printf("%s\n",buf);
}

你可能感兴趣的:(Linux-C-day-5-读写锁)