非阻塞io与记录锁

非阻塞io

1.对比
阻塞io的例子:scanf从终端获取输入时,如果不输入程序就会一直停在那; 对一个已经有写锁的文件请求读时, 会一直空等直到前面的进程释放锁...
非阻塞的例子:读取文件内容, 如果文件当前因加锁无法访问则立即出错返回

2.非阻塞io设置方法
a.调用open函数时, 指定O_NONBLOCK标志
open(filename,O_RDWR,O_NONBLOCK)
b.已经打开的fd, 调用fcntl将fd的O_NONBLOCK标志打开

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
 
char buf[500000];
 
void set_fl(int fd,int flags){
    int val;
    if((val=fcntl(fd,F_GETFL,0))<0){
        perror("fcntl F_GETFL error");
        exit(1);
    } 
    val|=flags; 
    if(fcntl(fd,F_SETFL,val)<0){
        perror("fcntl F_SETFL error");
        exit(1);
    }
}

void clr_fl(int fd,int flags){
    int val;
    if((val=fcntl(fd,F_GETFL,0))<0){
        perror("fcntl F_GETFL error");
        exit(1);
    } 
    val &= ~flags; 
    if(fcntl(fd,F_SETFL,val)<0){
        perror("fcntl F_SETFL error");
        exit(1);
    }
}
 
int main(){
    int ntowrite,nwrite;
    char *ptr;
 
    ntowrite=read(STDIN_FILENO,buf,sizeof(buf));
    fprintf(stderr,"read %d bytes
",ntowrite);
 
    set_fl(STDOUT_FILENO,O_NONBLOCK);
 
    ptr=buf;
    while(ntowrite>0){
        errno=0;
        nwrite=write(STDOUT_FILENO,ptr,ntowrite);
        fprintf(stderr,"nwrite=%d,errno=%d
",nwrite,errno);
 
        if(nwrite>0){
            ptr += nwrite;
            ntowrite -= nwrite;
        }
    }
 
    clr_fl(STDOUT_FILENO,O_NONBLOCK);
    return 0;
}

执行 ./a.out < /etc/services 2>stderr.out
查看stderr.out文件会发现write函数出现了很多次相同错误,
在centos上, 出错的errno=11, Resource temporarily unavailable
书本上测试的为errno=35, Resource deadlock avoided
从结果上看, 在写入stdout时, 由于终端缓冲的原因没办法一次write完成, 所以分成很多次去写
因为设置了O_NONBLOCK, 所以提交write任务后, 如果当前终端还在处理上一个循环的write, 则本次write立即返回并设置errno

记录锁(文件锁).

1.记录锁用于多进程对文件编辑的保护

2.锁函数

int fcntl(int fd, int cmd, struct flock *lock);

cmd = F_GETLK,测试能否建立一把锁
cmd = F_SETLK,设置锁
cmd = F_SETLKW, 阻塞设置一把锁

struct flock {
      short l_type;    /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
      short l_whence;  /* 加锁的起始位置:SEEK_SET, SEEK_CUR, SEEK_END */
      off_t l_start;   /* 加锁的起始偏移,相对于l_whence */
      off_t l_len;     /* 上锁的字节数*/
      pid_t l_pid;     /* 已经占用锁的PID(只对F_GETLK 命令有效) */
      /*...*/
};

3.锁的继承和释放
a.进程结束后, 进程所建立的锁全部释放
b.fork产生的子进程不继承父进程的锁
c.执行exec后, 新程序可以继承原执行程序的锁

4.建议锁和强制锁
建议锁:只关注fcntl加锁函数, open/read/write可以无视文件是否加锁
强制锁:read/write/open函数执行时都要检查文件是否加锁

值得注意的是, 强制锁的实现依赖系统

下面的程序运行二个实例, 第二个实例会在lockfile那一步报错

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int lockfile(int fd){
    struct flock fl; 
    fl.l_type=F_WRLCK;
    fl.l_start=0;
    fl.l_whence=SEEK_SET;
    fl.l_len=0;
    return(fcntl(fd,F_SETLK,&fl));
}
#define FILE_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
int main(){
    int fd;
    char *filename="./test.txt";
    struct stat statbuf;
    if((fd=open(filename,O_RDWR|O_CREAT,FILE_MODE))<0){
        perror("open error");
        exit(1);
    }
    if(write(fd,"abcdef",6) != 6){
        perror("write error");
        exit(1);
    }
/*
 *  if(fstat(fd,&statbuf)<0){
 *      perror("fstat error");
 *      exit(1);
 *  }
 *
 *  if(fchmod(fd,(statbuf.st_mode & ~S_IXGRP) | S_ISGID) <0){
 *      perror("fchmod error");
 *      exit(1);
 *  }
 */
    if(lockfile(fd)<0){
        perror("lockfile error");
        exit(1);
    }
    while(1){
        ;
    }
}
原文地址:https://www.cnblogs.com/cfans1993/p/5657476.html