• 【Linux】如何在进程间加锁(实现互斥)



    前言

    【Linux】初识进程间通信:建议先读完这篇。

    进程之间如何加锁,今天我们需要实现一个售票系统,我们需要对同一个num变量++。
    以往我们写过类似的代码,只需要用pthread_mutex_t 这把锁就可以实现一个进程内多个线程互斥。但是我们这里改变需求,假设是你和你的同学需要抢一张票,你们在不同的主机的相同进程抢票,那么要怎么做呢?

    mmap 配合 pthread_mutex_t


    pthread_mutex_t要使用,需要pthread_mutexattr_t的帮助,将原先的线程锁换为进程锁。
    mmap 这个函数功能是在共享区当中申请一块内存空间。
    是将用户空间的一段内存区域映射到内核空间,映射成功之后,用户对这段内存区域的修改可以直接反映到内核空间。

    先让多个进程能够看到一个num


    那么我们可以定义一个结构体,mt结构体,其中num标识总票数,mutex是互斥锁,mutexattr是锁的属性。

    void *mmap(void *addr, size_t length, int prot, int flags,
    int fd, off_t offset);

    struct mt
    {
        int num; //多个进程的票数
        pthread_mutex_t mutex;
        pthread_mutexattr_t mutexattr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    mt 对齐,结构体对象对齐时按照其最大的对齐数进行对齐。 这里推荐把mutexattr写到mutex前面,因为这样总体消耗的内存最小。(56 -》 48)
    在共享内存开辟mt空间大小,返回指针给两个进程。两个进程得到的指针值相同。

    由于此处是父子进程,不需要依赖文件描述符,fd填写为-1,MAP_SHARED|MAP_ANON的组合标识需要保存的文件并且是匿名的mmap,仅在进程间使用。PROT_READ|PROT_WRITE标识这块共享区的读写权限。

    匿名的mmap:

    • 适合父子进程使用
    mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
    
    • 1

    有名的mmap:

    • 打开一个文件,获取文件描述符
    • ftruncate进行设置文件大小,将文件大小设置成struct mt的大小,若原先文件大小超过sizeof(*mm),则会被截断为sizeof(*mm),若文件大小小于,则会扩到sizeof(*mm)
    • mmap进行设置,将共享内存当中开辟一块内存空间。
    • 关闭打开的文件描述符
    • 删除文件

    注意 :MAP_SHARED不进行设置的话后续会报段错误。
    这引入我们对flags变量的思考:

    • 若是设置成0,那么由于内核暂不支持,所以返回值会是MAP_FAILED,(void*)-1,也就是0xffffffffffffffff,那么后续对返回值做操作,都会出现段错误。
      在这里插入图片描述在这里插入图片描述
    • 若是设置为MAP_SHARED,两个进程是能够互斥访问的。在这里插入图片描述
    • 若是设置成MAP_PRIVATE,明显加的不是一个变量,说明父子发生写时拷贝,操作系统会在共享区开辟一块同属性的内存,让父进程或者子进程去读写。达不到进程同步。
      在这里插入图片描述
     int fd = open("mt_test",O_CREAT|O_RDWR,0777);// 打开文件,获得一个fd
        if( fd == -1 ) 
        {
            perror("open file:"); 
            exit(1); 
        }
    int n = ftruncate(fd,sizeof(*mm)); // 截取fd的部分
        mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        close(fd);
        unlink("mt_test"); // 删除文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    两种创建mmap的方式是一种万金油,任意进程都可以使用,另一种适合父子通信。

    多个进程互斥访问


    pthread_mutex默认是线程间通信加的锁,要是想用在进程之间,需要pthread_mutexattr_t *restrict attr 填充这个字段,在属性字段当中修改为PTHREAD_PROCESS_SHARED标识进程间共享。

     pthread_mutexattr_init(&mm->mutexattr);        
      // 初始化 mutex 属性
     pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);               
     // 修改属性为进程间共享
     
    pthread_mutex_init(&mm->mutex,&mm->mutexattr);      
    // 初始化一把 mutex 锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上述工作过后,已经在共享区创建了一个结构体,并且父进程已经接受了指向共享区的指针,fork创建子进程之后父子进程都可以挂接在共享区上面。

    在这里插入图片描述

    接下来,父子实现互斥访问就简单了。使用之前只需要先用mm->mutex进行加锁即可。同理,若是放着的是原子变量,自旋锁,读写锁,也都是可以使用的。

    pid = fork();
        if( pid == 0 )          // 子进程
        {
            for( i=0; i<10;i++ )
            {
                pthread_mutex_lock(&mm->mutex);
                (mm->num)++;
                printf("-child--------------num++    %d\n",mm->num);
                pthread_mutex_unlock(&mm->mutex);
                sleep(1);
            }
        
        }
        else 
        {
            for( i=0;i<10;i++)
            {
                sleep(1); 
                pthread_mutex_lock(&mm->mutex);
                mm->num += 2;
                printf("--------parent------num+=2   %d\n",mm->num);
                pthread_mutex_unlock(&mm->mutex);
            
            }
            wait(NULL);
     
        }
        pthread_mutexattr_destroy(&mm->mutexattr);  // 销毁 mutex 属性对象
        pthread_mutex_destroy(&mm->mutex);          // 销毁 mutex 锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    具体代码

    /*
        互斥量 实现 多进程 之间的同步 
    */
     
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
     
    struct mt
    {
        int num;
        pthread_mutex_t mutex;
        pthread_mutexattr_t mutexattr;
    };
     
    // 通过父子进程对同一个变量 mm->num 进行++ ,父进程一次+2 ,子进程一次 +1
    int main(void)
    {
        
        int i;
        struct mt* mm;
     
        pid_t pid;
        
    
        // 创建映射区文件
        int fd = open("mt_test",O_CREAT|O_RDWR,0777);
        if( fd == -1 ) 
        {
            perror("open file:"); 
            exit(1); 
        }
        ftruncate(fd,sizeof(*mm));
        mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED ,fd,0);
        close(fd);
    
        unlink("mt_test");
    
    
        //就是这个mt结构体,当我们创建mt
        // 建立映射区 MAP_ANON匿名映射
        //mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
        //    printf("-------before memset------\n");
        //memset(mm,0x00,sizeof(*mm));
        //   printf("-------after memset------\n");
     
        pthread_mutexattr_init(&mm->mutexattr);         // 初始化 mutex 属性
        pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);               // 修改属性为进程间共享
     
        pthread_mutex_init(&mm->mutex,&mm->mutexattr);      // 初始化一把 mutex 锁
            
        
        pid = fork();
        if( pid == 0 )          // 子进程
        {
            for( i=0; i<10;i++ )
            {
                pthread_mutex_lock(&mm->mutex);
                (mm->num)++;
                printf("-child--------------num++    %d\n",mm->num);
                pthread_mutex_unlock(&mm->mutex);
                sleep(1);
            }
        
        }
        else 
        {
            for( i=0;i<10;i++)
            {
                sleep(1); 
                pthread_mutex_lock(&mm->mutex);
                mm->num += 2;
                printf("--------parent------num+=2   %d\n",mm->num);
                pthread_mutex_unlock(&mm->mutex);
            
            }
            wait(NULL);
     
        }
        pthread_mutexattr_destroy(&mm->mutexattr);  // 销毁 mutex 属性对象
        pthread_mutex_destroy(&mm->mutex);          // 销毁 mutex 锁
     
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    采用共享内存配合信号量


    信号量我们使用System V的这套接口。在说到具体的实现方案,不得不说分辨一下System V的信号量,Posix V的信号量,Posix还分为有名和无名的信号量。

    System V的特点是同时能操作多个信号量,接口使用难度比Posix要复杂,移植性不太好。
    Posix 是
    解释一下为什么Posix 无名信号量适合线程同步,因为sem_init是将信号集sem_t进行初始化,而若要实现进程间的互斥,需要多个进程看到sem_t,就需要借助其他手段让其他进程看到sem_t。

    System V的信号量是随内核的。
    Posix有名信号灯的值是随内核持续的。
    Posix无名信号灯的持续性却是不定的,因为他是基于内存的,如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它也会消失。
    总结:

    • 1、System V的信号量一般用于进程同步, 且是内核持续的, api为:semget、semctl、semop
    • 2、Posix的有名信号量一般用于进程同步, 有名信号量是内核持续的. 有名信号量的api为:sem_open、sem_close、sem_unlink
    • 3、Posix的无名信号量一般用于线程同步, 无名信号量是进程持续的, 无名信号量的api为:sem_init、sem_destroy

    先介绍一下接口:

    semget

    int semget(key_t key, int nsems, int semflg);

    • key通常可以由ftok创建
    • nsems标识需要创建多少个信号量,我通常只需要操纵一个,所以这里填写1
    • semflg标识信号量的权限,服务端通常IPC_CREAT | IPC_EXCL都带上,标识创建一个新的IPC资源,客户端可以填0。
    • 返回值就是semid,标识内核维护的IPC资源的下标(操作系统是用数组维护IPC资源的ipc_perm),出错返回-1.
      在这里插入图片描述

    在这里插入图片描述

    semctl

    int semctl(int semid, int semnum, int cmd, …);

    • semid:就是semget获得的返回值
    • semnum:semget传参的时候nsems,这里要操作的就是0~nsems-1的信号集
    • cmd:标识对内核维护的信号量做什么操作,如:SETVAL,IPC_RMID,注意初始化值的时候应用SETVAL并非IPC_SET,IPC_SET是指用户层创建了一个结构体,用这个结构体的字段重新多创建一个IPC资源(我的理解,仅供参考)
    • …:这里的可变参数需要我们传多一个用户层自定义的结构体,这里参考内核的union semun,我们定义了semun后,设置val等同于设置该信号量为几元信号量,犹如设置1,那么他就可以为0或者1,也称之为二元信号量搭配SETVAL使用。这里我们只是实现互斥,所以此处填写1。
    • 返回值:失败返回-1

    下方为内核,实际我们编写的时候可以只有int val这个字段。

    union semun {
     int     val;   
      /* Value for SETVAL */
    struct semid_ds *buf;    
    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  
    /* Array for GETALL, SETALL */
     struct seminfo  *__buf;  
     /* Buffer for IPC_INFO
                                               (Linux-specific) */
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    semop

    int semop(int semid, struct sembuf *sops, unsigned nsops);

    • semid:semget返回值
    • sops:sem_num 标识需要操作的信号集具体是哪个,如果只创建了一个,那么这里是0;sem_op标识进行何种操作,此处填写-1标识获取当前信号量,填写1标识归还信号量;sem_flg表示以何种方式进行semop操作,若是阻塞,则填0,还有类似IPC_NOWAIT,SEM_UNDO(通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时)等字段
    • nsops标识操作第几块IPC资源:这里若semget填写的是1,这里也填写1,标识操作第一块信号量,这里就不是从0开始了。
    • 返回值:-1标识错误
    struct sembuf{
    unsigned short sem_num;  /* semaphore number */
    short          sem_op;   /* semaphore operation */
    short          sem_flg;  /* operation flags */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    核心逻辑


    父进程创建共享内存,创建信号量,由信号量标识当前资源是否被使用,由共享内存将真正的数据(ticket)放到共享区当中。当父子任意进程访问ticket,都需要semop获得信号量,这样就能保证临界资源受到保护了。

    需要注意的是,可以将信号量,共享内存在fork之前创建,之后调用接口不会引起写时拷贝。

    --*pticket;不要写成 *pticket --; Linux下会出现问题!!!!

    • semop(semid, &sb, 1) == -1 && EAGAIN == errno可以用来标识semctl删除信号量,op操作自然会失败,errno为EAGAIN。semop(semid, &sb, 1) == -1 也可以作为判断。
    • 最终票数为0,父子进程都调用semctl删除共享资源,只有一方会调用成功,当父子进程都semdt取消挂接后该资源便会释放。共享内存和信号量都需要调用对应接口,否则会造成资源泄露,都是随内核的。
    #include 
    using namespace std;
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    union semun
    {
        int val;               /* value for SETVAL */
        struct semid_ds *buf;  /* buffer for IPC_STAT, IPC_SET */
        unsigned short *array; /* array for GETALL, SETALL */
        struct seminfo *__buf; /* buffer for IPC_INFO */
    };
    int main()
    {
        int key = ftok("sem_sys.cc", 9000);
        int shm_key = ftok(".", 8080);
        int shmid = shmget(shm_key, 4096, IPC_CREAT | IPC_EXCL | 0644);
        if (shmid < 0)
        {
            perror("shmget");
            return 1;
        }
        int *pticket = (int *)shmat(shmid, nullptr, 0);
    
        *pticket = 1000; // 1000张票
    
        int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0644);
     
        if (semid < 0)
        {
            perror("semget");
            return 2;
        }
        semun se;
        se.val = 1;//标识单个信号量的资源大小,互斥填1,这里就会两态。
        // SETVAL   IPC_SET
        int res = semctl(semid, 0, SETVAL, se);
        if (res == -1)
        {
            perror("semctl");
            return 4;
        }
     
        pid_t id = fork();
        if (id == 0)
        {
            while (1)
            {
                // child
                struct sembuf sb; // P
                sb.sem_flg = 0;   //阻塞拿
                sb.sem_num = 0;   //拿第一个
                sb.sem_op = -1;   //获取信号量
    
                if (semop(semid, &sb, 1) == -1 && EAGAIN == errno)
                    break;
                // cout << "child " << *pticket_child << endl;
                //获得了这个信号量
                if (*pticket <= 0)
                {
                    semctl(semid, 0, IPC_RMID);
                    break;
                }
                --*pticket;
                cout << "child get ticket: " << *pticket << endl;
    
                usleep(1);
    
                // V
                struct sembuf sb2;
                sb2.sem_flg = 0;
                sb2.sem_num = 0;
                sb2.sem_op = 1;//释放信号量
                semop(semid, &sb2, 1);
            }
            shmdt(pticket);
        }
        else
        {
            while (1)
            {
                // father
                struct sembuf sb; // P
                sb.sem_flg = 0;
                sb.sem_num = 0;
                sb.sem_op = -1;
    
                if (semop(semid, &sb, 1) == -1 && EAGAIN == errno)
                    break;
    
                //获得了这个信号量
                // cout << "father " << *pticket_father << " :" << &pticket_father << endl;
                if (*pticket <= 0)
                {
                    semctl(semid, 0, IPC_RMID);
                    break;
                }
                --*pticket;
                cout << "father get ticket: " << *pticket << " :" << &pticket << endl;
    
                usleep(1);
    
                // V
                struct sembuf sb2;
                sb2.sem_flg = 0;
                sb2.sem_num = 0;
                sb2.sem_op = 1;
                semop(semid, &sb2, 1);
            }
            shmdt(pticket);
            shmctl(shmid, IPC_RMID, nullptr);
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121

    一开始担心会发生写时拷贝写的版本,让子进程让出时间片给父进程创建信号量和共享内存,子进程后进行挂接。

    #include 
    using namespace std;
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    union semun
    {
        int val;               /* value for SETVAL */
        struct semid_ds *buf;  /* buffer for IPC_STAT, IPC_SET */
        unsigned short *array; /* array for GETALL, SETALL */
        struct seminfo *__buf; /* buffer for IPC_INFO */
    };
    int main()
    {
        pid_t id = fork();
        if (id == 0)
        {
            usleep(100); //让父进程先启动
            int key = ftok("sem_sys.cc", 9000);
            int shm_key = ftok(".", 8080);
            //child 负责连接
            int shmid = shmget(shm_key, 0, 0);//获取共享内存
            if(shmid < 0)
            {
                perror("shmget");
                return 1;
            }
            int *pticket_child = (int *)shmat(shmid, nullptr, 0);//获取ticket
    
            int semid = semget(key, 1, 0);//获取信号量
            //cout << "child semid " << semid <
            if(semid < 0)
            {
                perror("semget");
                return 2;
            }
            while (1)
            {
                // child
                struct sembuf sb; // P
                sb.sem_flg = 0;   //阻塞拿
                sb.sem_num = 0;   //拿第一个
                sb.sem_op = -1;   //拿一个
    
                if (semop(semid, &sb, 1) == -1 && EAGAIN == errno)
                    break;
                //cout << "child " << *pticket_child << endl;
                //获得了这个信号量
                if (*pticket_child <= 0)
                {
                    semctl(semid, 0, IPC_RMID);
                    break;
                }
                --*pticket_child;
                cout << "child get ticket: " << *pticket_child << endl;
    
                usleep(1);
    
                // V
                struct sembuf sb2;
                sb2.sem_flg = 0;
                sb2.sem_num = 0;
                sb2.sem_op = 1;
                semop(semid, &sb2, 1);
            }
            shmdt(pticket_child);
        }
        else
        {
            int key = ftok("sem_sys.cc", 9000);
            int shm_key = ftok(".", 8080);
            int shmid = shmget(shm_key, 4096, IPC_CREAT | IPC_EXCL | 0644);
            if(shmid < 0)
            {
                perror("shmget");
                return 1;
            }
            int *pticket = (int *)shmat(shmid, nullptr, 0);
    
            *pticket = 1000; // 1000张票
    
            int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0644);
            //cout << "father semid " << semid <
            if(semid < 0)
            {
                perror("semget");
                return 2;
            }
            semun se;
            se.val = 1;
            // SETVAL   IPC_SET
            int res = semctl(semid, 0, SETVAL, se);
            if(res == -1)
            {
              perror("semctl");
              return 4;
            }
            int *pticket_father = pticket;
            while (1)
            {
                // father
                struct sembuf sb; // P
                sb.sem_flg = 0;
                sb.sem_num = 0;
                sb.sem_op = -1;
    
                if (semop(semid, &sb, 1) == -1  && EAGAIN == errno)
                    break;
    
                //获得了这个信号量
                //cout << "father " << *pticket_father << " :" << &pticket_father << endl;
                if (*pticket_father <=   0)
                {
                    semctl(semid, 0, IPC_RMID);
                    break;
                }
                --*pticket_father;
                cout << "father get ticket: " << *pticket_father << " :" << &pticket_father << endl;
    
                usleep(1);
    
                // V
                struct sembuf sb2;
                sb2.sem_flg = 0;
                sb2.sem_num = 0;
                sb2.sem_op = 1;
                semop(semid, &sb2, 1);
            }
            shmdt(pticket_father);
            shmctl(shmid,IPC_RMID,nullptr);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

    管道

    【Linux】初识进程间通信
    之前写的这篇文章由叙述,管道在65536内是支持原子性的,小于PIPE_BUF都能保证原子性,读写只能一端运行,内部就实现了互斥同步,要双向通信就开两个即可。

    参考:
    被遗忘的桃源——flock 文件锁
    进程间锁:进程间pthread_mutex,文件锁

    总结


    • 实际上我个人偏向于mmap的这种方式,因为当进程crash的时候,mmap可以保存到文件,但他和shm面临着共享内存被锁上无法使用的问题。
    • mmap和共享内存二者本质上是类似的,mmap可以看到文件的实体,而 shmget 对应的文件在交换分区上的 shm 文件系统内,无法直接cat 查看。
    • 因为文件锁flock在进程crash时,操作系统会清理掉,所以比较推荐使用flock作为进程的锁资源。(可以参考当中读读那个文章。)
    • 而信号量本身不能传递复杂消息,只能用来同步互斥。

    进程间加锁目前我所知道的以上三种方式已经成列,由于能力有限,若有错误,欢迎评论区指出。

  • 相关阅读:
    持久内存BTT实现及优化(一)
    基于约束的装配设计【CadQuery】
    【SQL 初级语法 1】 查询基础
    推荐一款AI写作大师、问答、绘画工具-「智元兔 AI」
    Synchronized锁升级
    iOS端如何实现MobLink的场景还原功能
    排队算法的matlab仿真,带GUI界面
    English语法_介词 - by
    c语言初学者用vs还是vscode?
    bp神经网络预测模型实例,bp神经网络模型的建立
  • 原文地址:https://blog.csdn.net/weixin_52344401/article/details/126742187