进程间通信——互斥

多个进程同步访问同一个资源和多个线程同步访问同一个资源相同,也会存在资源抢占的情况。所以需要进行资源加锁。

1. sem_open和sem_init的区别

适用范围sem_open用于获取一个命名信号量,而sem_init用于获取一个未命名的信号量
使用场景sem_open用于跨进程共享信号量的场景,sem_init用于在一个进程中初始化信号量,例如在一个线程中同步访问共享资源
参数sem_open需要执行信号量的名称和权限,而sem_init只需要指定信号量的地址初始参数
返回值sem_open返回指向信号量的指针,sem_init没有返回值

总结下来主要是在多个进程中要使用sem_open来创建信号量

1.1 sys/sem和semaphore

<sys/sem.h>为 XSI(最初是 Unix System V)信号量提供接口。 这些不是基本 POSIX 标准的一部分(它们在 XSI 选项中,主要是为了传统的 Unix 兼容性),虽然它们还没有被认为是过时的/弃用的,但许多程序员认为它们已弃用。

<semaphore.h>定义了 POSIX 信号量,它们的设计方式使得它们可以完全在用户空间中实现,除非在有争议的情况下,进程将调用内核进入睡眠状态。 它们的性能应该接近最佳(即几乎不可能击败您自己的),但它们的功能不如 XSI 信号量

它们彼此不同/替代,而是两种不同的实现并提供不同的功能集。semaphore.h是posix实现和sys/sem.h是 sysV 的实现。 POSIX 被 认为 更轻量级并被广泛使用。

2.使用方法

使用起,从创建到销毁来大概分为四步

2.1 创建命名信号量sem_open

sem_t *sem_open(const char *name, int oflag, ...);
参数:
name:信号量的名字;
oflag:打开标志,常用的有 O_CREAT 和 O_EXCL。O_CREAT 表示如果名字的信号量不存在则创建它,而 O_EXCL 则在创建新信号量时使用,如果名字的信号量已经存在则返回错误
...:代表当使用 O_CREAT 标志时,用于指定信号量初始值的参数。如果没有使用 O_CREAT,则此参数被忽略。

2.2等待信号量sem_wait

sem_wait() 用于等待信号量,它会使调用进程等待,直到信号量的值大于零,其定义如下:

int sem_wait(sem_t *sem);

@参数
sem;指向信号量的指针

@返回值
若函数执行成功,返回 0,失败返回-1

2.3 释放信号量sem_post

sem_post()用于当一个进程完成对共享资源的访问后释放信号量,这样其他等待该信号量的进程就可以继续执行。其定义如下:

int sem_post(sem_t *sem);
@参数
sem;指向信号量的指针

@返回值
若函数执行成功,返回 0,失败返回-1

2.4关闭信号量sem_close

sem_close()用于关闭已打开的信号量,其定义如下:

int sem_close(sem_t *sem);

其中,sem为指向已打开的信号量的指针。
若函数执行成功,返回 0,失败返回-1

2.5删除命名信号量sem_unlink

sem_unlink() 用于当一个命名信号量不再需要时,将其从系统中删除。其定义如下:

int sem_unlink(const char *name);

name为信号量的名字。
若函数执行成功,返回 0,失败返回-1

‌一个进程使用sem_unlink删除信号量后,另一个进程仍然可以尝试使用sem_unlink。‌这是因为当一个进程调用sem_unlink时,它会尝试删除指定的信号量名。但是,这个删除操作并不会立即生效,而是要等到所有其他进程都关闭了该信号量之后,信号量才会被真正删除。

但是当进程异常退出而没有调用sem_close时,信号量是无法被系统回收的

2.6 sem_closesem_unlink的差别

  • sem_close‌函数用于关闭一个已经打开的有名信号量。当一个进程终止时,内核会自动对其上仍然打开着的所有有名信号量执行sem_close操作。关闭一个信号量并不会将它从系统中删除,而是结束对该信号量的引用。因此,sem_close操作只是结束了当前进程对信号量的使用,但信号量本身并未被删除‌12。
  • sem_unlink‌函数则用于从系统中删除一个有名信号量。每个信号量有一个引用计数器记录打开的次数,当引用计数大于0时,信号量的名字可以从文件系统中删除,然而其析构却要等到最后一个sem_close发生时为止。因此,sem_unlink操作实际上是从文件系统中移除信号量的记录,而真正的析构发生在最后一个对信号量的sem_close操作之后‌1。
    简而言之,‌sem_close‌主要是结束进程对信号量的使用,而‌sem_unlink‌则是从系统中移除信号量的记录,两者共同作用,确保了信号量的正确管理和资源的有效利用。

3.代码实例

read.exe

#include <sys/types.h>
#include <sys/ipc.h> 
#include <sys/shm.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define KEY_NUM 126
#define FILE_PATH "./share.txt"

//使用文件路径作为共享内存
int shm_create_with_key(const char *filepath, int len, int key_num)
{
	key_t key = ftok(filepath, key_num);
	if(key < 0)
	{
		printf("create key error!\n");
		return key;
	}
	int shmid = shmget(key, len, IPC_CREAT|0666);//666代表可读可写
	if(shmid < 0)
	{
		return shmid;
		printf("create share mem error!\n");
	}
	return shmid;
}

int shm_create(int len)
{
	return shm_create_with_key(FILE_PATH, 100, KEY_NUM);
}

char* shm_connect(int shm_id)
{
	char *mem = (char*)shmat(shm_id, NULL, 0);
	if(mem < 0)
	{
		printf("shmat error!\n");
		return NULL;
	}
	return mem;
}

int main()
{
    //创建打开一个信号量
    sem_t *sem = sem_open("m_sem2", O_CREAT, 0666, 1);
    if(sem == NULL)
    {
        printf("sem_open error!\n");
        return -1;
    }
    int shm_id = shm_create(10);
    if(shm_id < 0)
    {
        printf("shm_create error!\n");
        return -1;
    }   
    char *mem = shm_connect(shm_id);
    if(!mem)
    {
        printf("shm_connect error!\n");
        return -1;
    }
    int i = 5;
    while (--i)
    {
        int num=0;
        if(0 == sem_getvalue(sem, &num) && 0 == num)
        {
            printf("read.exe waiting resource\n");
        }
        sem_wait(sem);
        printf("%s\n", mem);
        sem_post(sem);
        sleep(1);

    }

    shmdt(mem);//释放共享内存
    shmctl(shm_id, IPC_RMID, NULL);//删除共享内存
    sem_close(sem);//关闭信号量
    sem_unlink("m_sem2");//删除信号量
    return 0;
}

write.exe

#include <sys/types.h>
#include <sys/ipc.h> 
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "string.h"

#define KEY_NUM 126
#define FILE_PATH "./share.txt"

int shm_create_with_key(const char *filepath, int len, int key_num)
{
	key_t key = ftok(filepath, key_num);
	if(key < 0)
	{
		printf("create key error!\n");
		return key;
	}
	int shmid = shmget(key, len, IPC_CREAT|0666);//666代表可读可写
	if(shmid < 0)
	{
		return shmid;
		printf("create share mem error!\n");
	}
	return shmid;
}

int shm_create(int len)
{
	return shm_create_with_key(FILE_PATH, 100, KEY_NUM);
}


char* shm_connect(int shm_id)
{
	char *mem = (char*)shmat(shm_id, NULL, 0);
	if(mem < 0)
	{
		printf("shmat error!\n");
		return NULL;
	}
	return mem;
}

int main()
{
    sem_t *sem = sem_open("m_sem2", O_CREAT, 0666, 1);
    if(NULL == sem)
    {
        printf("sem_open error\n");
        return -1;
    }
    int shm_id = shm_create(10);
    if(shm_id < 0)
    {
        printf("shm_create error!\n");
        return 0;
    }   
    char *mem = shm_connect(shm_id);
    if(!mem)
    {
        printf("shm_connect error!\n");
        return 0;
    }
    //初始化
    memcpy(mem, "hello", 6);
    int i = 5;
    while (--i)
    {
        sleep(1);
        int num=0;
        if(0 == sem_getvalue(sem, &num) && 0 == num)
        {
            printf("write.exe waiting resource\n");
        }
        sem_wait(sem);
        mem[0] = '1'+i;
        printf("write succ\n");
        sleep(2);
        sem_post(sem);
        
    }

    shmdt(mem);
    sem_close(sem);
    sem_unlink("m_sem2");
    return 0;
}
s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值