Linux编程–进程同步

1、pthread_mutex_t实现进程锁

  • 创建进程:fork
  • 内存映射区:mmap
  • 创建互斥锁:pthread_mutex_init
  • 创建互斥锁属性:pthread_mutexattr_init

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

// 进程共享数据模型
struct model{
    int num;
    pthread_mutex_t mutex;
    pthread_mutexattr_t attr;
};
int main(){
    int length = sizeof(struct model);
    /*
    int fd = open("temp.txt", O_CREAT | O_RDWR, 0664);
    if (fd < 0){
        perror("open error:");
        exit(1);
    }
    int ret = ftruncate(fd, length);
    if (ret < 0){
        perror("ftruncate error");
        exit(1);
    }
    */
    // 创建共享数据(内存映射)
    struct model *m = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (m == MAP_FAILED){
        perror("mmap error:");
        exit(1);
    }
    memset(m, 0, length);

    // 初始化mutex及mutexattr
    pthread_mutexattr_init(&m->attr);
    // mutex默认状态为线程互斥,通过attr设置为进程互斥
    pthread_mutexattr_setpshared(&m->attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&m->mutex, &m->attr);
    pthread_mutexattr_destroy(&m->attr);
    // 生产随机数种子
    srand(time(NULL));
    // 创建新进程
    pid_t pid = fork();
    if (pid < 0){
        perror("fork error:");
        exit(1);
    }else if (pid == 0){
        // 父进程
        int i = 10;
        while(i--){
            pthread_mutex_lock(&m->mutex);
            m->num += 1;
            printf("parent ---- %d\n", m->num);
            pthread_mutex_unlock(&m->mutex);
            usleep(rand()%500000);
        }
        // 回收子进程、互斥锁、内存映射
        wait(NULL);
        pthread_mutex_destroy(&m->mutex);
        munmap(m, length);
    }else{    // 子进程
        int i = 10;
        while(i--){
            pthread_mutex_lock(&m->mutex);
            m->num += 2;
            printf("child  ---- %d\n", m->num);
            pthread_mutex_unlock(&m->mutex);
            usleep(rand()%500000);
        }
    }
    return 0;
}

执行结果

parallels@ubuntu:~/Linux/process$ ./mutex.out
child  ---- 2
parent ---- 3
parent ---- 4
child  ---- 6
child  ---- 8
parent ---- 9
child  ---- 11
parent ---- 12
parent ---- 13
child  ---- 15
child  ---- 17
parent ---- 18
child  ---- 20
parent ---- 21
child  ---- 23
parent ---- 24
child  ---- 26
parent ---- 27
child  ---- 29
parent ---- 30
parallels@ubuntu:~/Linux/process$

2、fcntl实现文件锁

  • F_SETLK:设置文件锁(trylock)
  • F_SETLKW:设置文件锁(lock)W->wait
  • F_GETLK:获取文件锁
int fcntl(int fd, int cmd, ... /* arg */ );

F_SETLK, F_SETLKW, and F_GETLK are used to acquire, release, and test for the existence of record locks (also known as byte-range, file-segment, or
       file-region locks).  The third argument, lock, is a pointer to a structure that has at least the following fields (in unspecified order).

           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) */
               ...
           };

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[]){
    // 要求传入需要加锁的文件路径
    if (argc < 2){
        printf("e.g: ./a.out filename\n");
        exit(1);
    }
    const char* filename = argv[1];
    // 初始化文件锁结构体
    struct flock lock;
    //lock.l_type = F_RDLCK;     // 加锁类型(读或写)
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;    // 加锁位置(当前位置、起始位置、末尾位置)
    lock.l_start = 0;            // 开始位置偏移量
    lock.l_len = 0;              // 加锁长度(单位字节),0则全部加锁
    int fd = open(filename, O_RDWR);
    if (fd < 0){
        perror("open error");
        exit(1);
    }
    // 设置文件锁:SETLKW,阻塞形式设置(加锁还是解锁取决于结构体lock中的值)
    fcntl(fd, F_SETLKW, &lock);
    printf("file is locked with 'F_RDLCK'\n");
    sleep(10);
    // 修改结构体参数
    lock.l_type = F_UNLCK;
    // 设置文件锁(解锁)
    fcntl(fd, F_SETLKW, &lock);
    printf("file is unlock...\n”);
    // 关闭文件
    close(fd);
    return 0;
}

执行结果:
* 在锁为读的情况下,多进程可以对单个文件同时操作;
* 在锁为写的情况下,多进程会被阻塞等待,只有上一个进程解锁后,下一个才能进行加锁

Leave a Reply