zoukankan      html  css  js  c++  java
  • Linux 读写锁

    线程的读写锁函数:

    1,读写锁的初始化与销毁,静态初始化的话,可以直接使用PTHREAD_RWLOCK_INITIALIZER。

    #include <pthread.h>
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
               const pthread_rwlockattr_t *restrict attr);
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
    

    2,用读的方式加锁和尝试(没锁上就立即返回)加锁。

    #include <pthread.h>
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    

    3,用写的方式加锁和尝试(没锁上就立即返回)加锁。

    #include <pthread.h>
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    

    4,解锁

    #include <pthread.h>
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    

    多个进程在同时读写同一个文件,会发生什么?

    例子1:用下面的例子的执行结果,观察多个进程在同时读写同一个文件,会发生什么。

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MAXLINE 100
    #define FN "num1"
    
    void my_lock(int fd){
      return;
    }
    
    void my_unlock(int fd){
      return;
    }
    
    int main(int args, char** argv){
    
      int fd;
      long i,seqno;
      pid_t pid;
      ssize_t n;
      char line[MAXLINE + 1];
    
      pid = getpid();
      fd = open(FN, O_RDWR, 0664);
    
      for(i = 0; i < 20; ++i){
        my_lock(fd);
    
        lseek(fd, 0L, SEEK_SET);
        n = read(fd, line, MAXLINE);
        line[n] = '';
    
        seqno = atol(line);
        printf("%s:pid = %ld, seq = %ld
    ", argv[0], (long)pid, seqno);
    
        seqno++;
    
        snprintf(line, sizeof(line), "%ld
    ", seqno);
    
        lseek(fd, 0L, SEEK_SET);
        write(fd, line, strlen(line));
    
        my_unlock(fd);
      }
    
      return 0;
    }
    

    执行方法:同时执行上面例子的程序2次,也就是2个进程同时读写同一个文件。

    ubuntu$ ./flockmain1 & ./flockmain1 &
    

    执行结果如下,发现2个进程同时读写,在①处开始,内核切换进程时,数字乱套了。

    ubuntu$ ./flockmain1 & ./flockmain1 &
    [1] 4760
    [2] 4761
    ubuntu$ ./flockmain1:pid = 4761, seq = 1
    ./flockmain1:pid = 4761, seq = 2
    ./flockmain1:pid = 4761, seq = 3
    ./flockmain1:pid = 4761, seq = 4
    ./flockmain1:pid = 4761, seq = 5
    ./flockmain1:pid = 4761, seq = 6
    ./flockmain1:pid = 4761, seq = 7
    ./flockmain1:pid = 4761, seq = 8
    ./flockmain1:pid = 4761, seq = 9
    ./flockmain1:pid = 4761, seq = 10   ------------①
    ./flockmain1:pid = 4760, seq = 10
    ./flockmain1:pid = 4761, seq = 11
    ./flockmain1:pid = 4761, seq = 12
    ./flockmain1:pid = 4761, seq = 13
    ./flockmain1:pid = 4761, seq = 14
    ./flockmain1:pid = 4761, seq = 15
    ./flockmain1:pid = 4761, seq = 16
    ./flockmain1:pid = 4761, seq = 17
    ./flockmain1:pid = 4761, seq = 18
    ./flockmain1:pid = 4761, seq = 19
    ./flockmain1:pid = 4761, seq = 20
    ./flockmain1:pid = 4760, seq = 11
    ./flockmain1:pid = 4760, seq = 12
    ./flockmain1:pid = 4760, seq = 13
    ./flockmain1:pid = 4760, seq = 14
    ./flockmain1:pid = 4760, seq = 15
    ./flockmain1:pid = 4760, seq = 16
    ./flockmain1:pid = 4760, seq = 17
    ./flockmain1:pid = 4760, seq = 18
    ./flockmain1:pid = 4760, seq = 19
    ./flockmain1:pid = 4760, seq = 20
    ./flockmain1:pid = 4760, seq = 21
    ./flockmain1:pid = 4760, seq = 22
    ./flockmain1:pid = 4760, seq = 23
    ./flockmain1:pid = 4760, seq = 24
    ./flockmain1:pid = 4760, seq = 25
    ./flockmain1:pid = 4760, seq = 26
    ./flockmain1:pid = 4760, seq = 27
    ./flockmain1:pid = 4760, seq = 28
    ./flockmain1:pid = 4760, seq = 29
    

    为了解决上面的问题,必须对文件的内容进行加锁。

    如何对文件内容加锁?

    使用fcntl函数,它既可以锁整文件,也可以锁文件里的某段内容。通过结构体flock来指定要锁的范围。如果 whence = SEEK_SET;l_start = 0;l_len = 0;就是锁定整个文件。

    struct flock {
                   ...
                   short l_type;    /* Type of lock: F_RDLCK,
                                       F_WRLCK, F_UNLCK */
                   short l_whence;  /* How to interpret l_start:
                                       SEEK_SET, SEEK_CUR, SEEK_END */
                   off_t l_start;   /* Starting offset for lock */
                   off_t l_len;     /* Number of bytes to lock */
                   pid_t l_pid;     /* PID of process blocking our lock
                                       (set by F_GETLK and F_OFD_GETLK) */
                   ...
               };
    
    
    • F_SETLK:上锁。如果发现已经被别的进程上锁了,就直接返回-1,errno被设置成EACCES或者EAGAIN,不阻塞。
    • F_SETLKW:上锁。阻塞等待。
    • F_GETLK:得到锁的状态。

    修改上面的函数my_lock,my_unlock。main函数不变。

    例子2:

    void my_lock(int fd){
      struct flock lock;
      lock.l_type = F_WRLCK;
      wlock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
      
      fcntl(fd, F_SETLKW, lock);
    }
    
    void my_unlock(int fd){
      struct flock lock;
      lock.l_type = F_UNLCK;
      lock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
    
      fcntl(fd, F_SETLK, lock);
    }
    

    执行结果如下,发现数字不乱套了。

    ubuntu$ ./flockmain & ./flockmain &
    [1] 4882
    [2] 4883
    ubuntu$ ./flockmain:pid = 4883, seq = 1
    ./flockmain:pid = 4883, seq = 2
    ./flockmain:pid = 4883, seq = 3
    ./flockmain:pid = 4883, seq = 4
    ./flockmain:pid = 4883, seq = 5
    ./flockmain:pid = 4883, seq = 6
    ./flockmain:pid = 4883, seq = 7
    ./flockmain:pid = 4883, seq = 8
    ./flockmain:pid = 4883, seq = 9
    ./flockmain:pid = 4883, seq = 10
    ./flockmain:pid = 4883, seq = 11
    ./flockmain:pid = 4883, seq = 12
    ./flockmain:pid = 4883, seq = 13
    ./flockmain:pid = 4883, seq = 14
    ./flockmain:pid = 4883, seq = 15
    ./flockmain:pid = 4883, seq = 16
    ./flockmain:pid = 4883, seq = 17
    ./flockmain:pid = 4883, seq = 18
    ./flockmain:pid = 4883, seq = 19
    ./flockmain:pid = 4883, seq = 20
    ./flockmain:pid = 4882, seq = 21
    ./flockmain:pid = 4882, seq = 22
    ./flockmain:pid = 4882, seq = 23
    ./flockmain:pid = 4882, seq = 24
    ./flockmain:pid = 4882, seq = 25
    ./flockmain:pid = 4882, seq = 26
    ./flockmain:pid = 4882, seq = 27
    ./flockmain:pid = 4882, seq = 28
    ./flockmain:pid = 4882, seq = 29
    ./flockmain:pid = 4882, seq = 30
    ./flockmain:pid = 4882, seq = 31
    ./flockmain:pid = 4882, seq = 32
    ./flockmain:pid = 4882, seq = 33
    ./flockmain:pid = 4882, seq = 34
    ./flockmain:pid = 4882, seq = 35
    ./flockmain:pid = 4882, seq = 36
    ./flockmain:pid = 4882, seq = 37
    ./flockmain:pid = 4882, seq = 38
    ./flockmain:pid = 4882, seq = 39
    ./flockmain:pid = 4882, seq = 40
    

    到此为止,貌似解决了问题,但是如果同时执行例子1和例子2,结果如下,发现还是乱的。

    也就是说在协作线程(cooperating processes)间,文件锁(也叫劝告性上锁)也起作用的。但是不完全不相关的进程中,文件锁也不起作用的。如何解决呢?使用强制性上锁。

    ys@ys-VirtualBox:~/IPC$ ./flockmain1 & ./flockmain &
    [1] 3602
    [2] 3603
    ys@ys-VirtualBox:~/IPC$ ./flockmain1:pid = 3602, seq = 1
    ./flockmain:pid = 3603, seq = 1
    ./flockmain:pid = 3603, seq = 2
    ./flockmain:pid = 3603, seq = 3
    ./flockmain:pid = 3603, seq = 4
    ./flockmain:pid = 3603, seq = 5
    ./flockmain:pid = 3603, seq = 6
    ./flockmain:pid = 3603, seq = 7
    ./flockmain:pid = 3603, seq = 8
    ./flockmain:pid = 3603, seq = 9
    ./flockmain:pid = 3603, seq = 10
    ./flockmain1:pid = 3602, seq = 2
    ./flockmain1:pid = 3602, seq = 3
    ./flockmain1:pid = 3602, seq = 4
    ./flockmain:pid = 3603, seq = 11
    ./flockmain:pid = 3603, seq = 12
    ./flockmain1:pid = 3602, seq = 5
    ./flockmain:pid = 3603, seq = 13
    ./flockmain1:pid = 3602, seq = 6
    ./flockmain1:pid = 3602, seq = 7
    ./flockmain1:pid = 3602, seq = 8
    ./flockmain:pid = 3603, seq = 14
    ./flockmain:pid = 3603, seq = 15
    ./flockmain1:pid = 3602, seq = 9
    ./flockmain1:pid = 3602, seq = 10
    ./flockmain:pid = 3603, seq = 16
    ./flockmain:pid = 3603, seq = 17
    ./flockmain1:pid = 3602, seq = 11
    ./flockmain:pid = 3603, seq = 18
    ./flockmain1:pid = 3602, seq = 12
    ./flockmain1:pid = 3602, seq = 13
    ./flockmain1:pid = 3602, seq = 14
    ./flockmain:pid = 3603, seq = 19
    ./flockmain:pid = 3603, seq = 20
    ./flockmain1:pid = 3602, seq = 15
    ./flockmain1:pid = 3602, seq = 16
    ./flockmain1:pid = 3602, seq = 17
    ./flockmain1:pid = 3602, seq = 18
    ./flockmain1:pid = 3602, seq = 19
    ./flockmain1:pid = 3602, seq = 20
    

    第一个问题:假如一个文件被一个进程以读的方式锁定,并有另一个进程在等待读锁定解锁后,用写入的方式锁定,这时是否允许另一个进程的还以读的方式取得锁定?

    用例子3来观察:

    #include <time.h>
    #include <sys/time.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    
    void gftime(char* buf){
      struct timeval tv;
      gettimeofday(&tv, NULL);
      long usec = tv.tv_usec;
      struct tm* tm = localtime(&tv.tv_sec);
      sprintf(buf, "%d:%d:%d.%ld",tm->tm_hour, tm->tm_min, tm->tm_sec,usec);
    
    }
    
    int main(){
      char buff[100] = {0};
     
      int fd = open("test.dat", O_RDWR | O_CREAT, 0664);
    
      struct flock lock;
      
      lock.l_type = F_RDLCK;
      lock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
      fcntl(fd, F_SETLK, &lock);
    
      gftime(buff);
      printf("%s: parent has read lock
    ", buff);
    
      //first child
      if(fork() == 0){
    
        char buf2[100] = {0};
        sleep(1);
        gftime(buf2);
        printf("%s: first child tries to obtain write lock
    ", buf2);
    
        struct flock lock2;
        lock2.l_type = F_WRLCK;
        lock2.l_whence = SEEK_SET;
        lock2.l_start = 0;
        lock2.l_len = 0;
        fcntl(fd, F_SETLKW, &lock2);
    
        gftime(buf2);
        printf("%s: first child obtains write lock
    ", buf2);
    
        sleep(2);
    
        lock2.l_type = F_UNLCK;
        lock2.l_whence = SEEK_SET;
        lock2.l_start = 0;
        lock2.l_len = 0;
        fcntl(fd, F_SETLK, &lock2);
    
        gftime(buf2);
        printf("%s: first child releases write lock
    ", buf2);
        
        exit(0);
      }
      //secodn child
      if(fork() == 0){
        char buf1[100] = {0};
        sleep(3);
        gftime(buf1);
        printf("%s: second child tries to obtain read lock
    ", buf1);
    
        struct flock lock1;
        lock1.l_type = F_RDLCK;
        lock1.l_whence = SEEK_SET;
        lock1.l_start = 0;
        lock1.l_len = 0;
        fcntl(fd, F_SETLKW, &lock1);
    
        gftime(buf1);
        printf("%s: second child obtains read lock
    ", buf1);
    
        sleep(4);
    
        lock1.l_type = F_UNLCK;
        lock1.l_whence = SEEK_SET;
        lock1.l_start = 0;
        lock1.l_len = 0;
        fcntl(fd, F_SETLK, &lock1);
    
        gftime(buf1);
        printf("%s: second child release read lock
    ", buf1);
        
        exit(0);
      }
    
      //parent process
      sleep(5);
    
      lock.l_type = F_UNLCK;
      lock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
      fcntl(fd, F_SETLK, &lock);
    
      gftime(buff);
      printf("%s: parent releases read lock
    ", buff);
      
      wait(NULL);
      wait(NULL);
    
      exit(0);
    }
    
    

    在ubuntu上执行结果:

    17:49:44.348946: parent has read lock
    17:49:45.350191: first child tries to obtain write lock
    17:49:47.350155: second child tries to obtain read lock
    17:49:47.350409: second child obtains read lock
    17:49:49.349442: parent releases read lock
    17:49:51.351197: second child release read lock
    17:49:51.351582: first child obtains write lock
    17:49:53.351689: first child releases write lock
    

    第一个问题的答案:允许另一个进程的还以读的方式取得锁定

    第二个问题:假如一个文件被一个进程以写的方式锁定,这时又有2个进程在等待这个锁的释放,其中一个进程是以写锁的方式等待,其中另一个进程是以读锁的方式等待,哪一个会优先取得锁?

    用例子4来观察:

    #include <time.h>
    #include <sys/time.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    
    void gftime(char* buf){
      struct timeval tv;
      gettimeofday(&tv, NULL);
      long usec = tv.tv_usec;
      struct tm* tm = localtime(&tv.tv_sec);
      sprintf(buf, "%d:%d:%d.%ld",tm->tm_hour, tm->tm_min, tm->tm_sec,usec);
    
    }
    
    int main(){
      char buff[100] = {0};
     
      int fd = open("test.dat", O_RDWR | O_CREAT, 0664);
    
      struct flock lock;
      
      lock.l_type = F_WRLCK;
      lock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
      fcntl(fd, F_SETLK, &lock);
    
      gftime(buff);
      printf("%s: parent has write lock
    ", buff);
    
      //first child
      if(fork() == 0){
    
        char buf2[100] = {0};
        sleep(1);
        gftime(buf2);
        printf("%s: first child tries to obtain write lock
    ", buf2);
    
        struct flock lock2;
        lock2.l_type = F_WRLCK;
        lock2.l_whence = SEEK_SET;
        lock2.l_start = 0;
        lock2.l_len = 0;
        fcntl(fd, F_SETLKW, &lock2);
    
        gftime(buf2);
        printf("%s: first child obtains write lock
    ", buf2);
    
        sleep(2);
    
        lock2.l_type = F_UNLCK;
        lock2.l_whence = SEEK_SET;
        lock2.l_start = 0;
        lock2.l_len = 0;
        fcntl(fd, F_SETLK, &lock2);
    
        gftime(buf2);
        printf("%s: first child releases write lock
    ", buf2);
        
        exit(0);
      }
      //secodn child
      if(fork() == 0){
        char buf1[100] = {0};
        sleep(3);
        gftime(buf1);
        printf("%s: second child tries to obtain read lock
    ", buf1);
    
        struct flock lock1;
        lock1.l_type = F_RDLCK;
        lock1.l_whence = SEEK_SET;
        lock1.l_start = 0;
        lock1.l_len = 0;
        fcntl(fd, F_SETLKW, &lock1);
    
        gftime(buf1);
        printf("%s: second child obtains read lock
    ", buf1);
    
        sleep(4);
    
        lock1.l_type = F_UNLCK;
        lock1.l_whence = SEEK_SET;
        lock1.l_start = 0;
        lock1.l_len = 0;
        fcntl(fd, F_SETLK, &lock1);
    
        gftime(buf1);
        printf("%s: second child release read lock
    ", buf1);
        
        exit(0);
      }
    
      //parent process
      sleep(5);
    
      lock.l_type = F_UNLCK;
      lock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
      fcntl(fd, F_SETLK, &lock);
    
      gftime(buff);
      printf("%s: parent releases write lock
    ", buff);
      
      wait(NULL);
      wait(NULL);
    
      exit(0);
    }
    

    在ubuntu上执行结果:

    17:49:29.796599: parent has write lock
    17:49:30.797099: first child tries to obtain write lock
    17:49:32.796885: second child tries to obtain read lock
    17:49:34.796868: parent releases write lock
    17:49:34.796987: second child obtains read lock
    17:49:38.797148: second child release read lock
    17:49:38.797297: first child obtains write lock
    17:49:40.797727: first child releases write lock
    

    第二个问题的答案:没有准确答案。在Ubuntu上的执行结果上看,读锁优先了,但是,可能在别的环境上又是写锁优先。按道理来说应该写锁优先吧?

    c/c++ 学习互助QQ群:877684253

    本人微信:xiaoshitou5854

  • 相关阅读:
    从表达式到变量:一行scheme代码之所见
    document 文挡对象详解(JavaScript脚本语言描述)
    开源Linux系统成为微软Vista竞争对手
    Java下的框架编程(5)cglib的应用
    如何做好职业规划(乾卦)
    Asianux将成为最先进的安全Linux操作系统
    开发人员行走Unix的随身四艺
    Java中类初始化的顺序
    用Java线程获取优异性能(II)——使用同步连载线程访问关键代码部份
    prototype.js的扩展
  • 原文地址:https://www.cnblogs.com/xiaoshiwang/p/11057552.html
Copyright © 2011-2022 走看看