Linux进程通信-信号量C++实现

一、什么是信号量

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个进/线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和释放(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

二、信号量的工作原理

由于信号量只能进行两种操作等待和释放信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行

三、Linux的信号量机制

1.semget函数

它的作用是创建一个新信号量或取得一个已有信号量

int semget(key_t key, int num_sems, int sem_flags);  

semget函数成功返回一个相应信号标识符(非零),失败返回-1.

2.semop函数

它的作用是改变信号量的值

int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
struct sembuf{  
    short sem_num;//除非使用一组信号量,否则它为0  
    short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,
				    //一个是-1,即P(等待)操作,  
                    //一个是+1,即V(释放信号)操作。  
    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,  
                    //并在进程没有释放该信号量而终止时,操作系统释放信号量  
};  

sem_op值的意义:
如果其值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op内含值的绝对值,通常用于获取资源的使用权(P操作)
如果其值为正数,该值会加到现有的信号内含值中,通常用于释放所控资源的使用权(V操作);
3.semctl函数

该函数用来直接控制信号量信息

int semctl(int sem_id, int sem_num, int command, ...);  

command命令主要是两种:
SETVAL:用来把信号量初始化为一个已知的值。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

四、进程使用信号量通信

下面使用一个例子来说明进程间如何使用信号量来进行通信,这个例子是两个相同的程序同时向屏幕输出数据,我们可以看到如何使用信号量来使两个进程协调工作,使同一时间只有一个进程可以向屏幕输出数据

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *arry;
};

static int sem_id = 0;

static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();

int main(int argc, char *argv[])
{
	char message = 'X';
	int i = 0;

	//创建信号量
	sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);

	if(argc > 1)
	{
		//程序第一次被调用,初始化信号量
		if(!set_semvalue())
		{
			fprintf(stderr, "Failed to initialize semaphore\n");
			exit(EXIT_FAILURE);
		}
		//设置要输出到屏幕中的信息,即其参数的第一个字符
		message = argv[1][0];
		sleep(2);
	}
	for(i = 0; i < 10; ++i)
	{
		//进入临界区
		if(!semaphore_p())
			exit(EXIT_FAILURE);
		//向屏幕中输出数据
		printf("%c", message);
		//清理缓冲区,然后休眠随机时间
		fflush(stdout);
		sleep(rand() % 3);
		//离开临界区前再一次向屏幕输出数据
		printf("%c", message);
		fflush(stdout);
		//离开临界区,休眠随机时间后继续循环
		if(!semaphore_v())
			exit(EXIT_FAILURE);
		sleep(rand() % 2);
	}

	sleep(10);
	printf("\n%d - finished\n", getpid());

	if(argc > 1)
	{
		//如果程序是第一次被调用,则在退出前删除信号量
		sleep(3);
		del_semvalue();
	}
	exit(EXIT_SUCCESS);
}

static int set_semvalue()
{
	//用于初始化信号量,在使用信号量前必须这样做
	union semun sem_union;

	sem_union.val = 1;
	if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
		return 0;
	return 1;
}

static void del_semvalue()
{
	//删除信号量
	union semun sem_union;

	if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
		fprintf(stderr, "Failed to delete semaphore\n");
}

static int semaphore_p()
{
	//对信号量做减1操作,即等待P(sv)
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = -1;//P()
	sem_b.sem_flg = SEM_UNDO;
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_p failed\n");
		return 0;
	}
	return 1;
}

static int semaphore_v()
{
	//这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = 1;//V()
	sem_b.sem_flg = SEM_UNDO;
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_v failed\n");
		return 0;
	}
	return 1;
}

编译后程序名为a.out。
启动多个a.out进程,可以观察到,它们在打印字符时,只会交替打印,而不会多个进程同时打印。

五、C++实现信号量

c++11中有 mutex (互斥量),有 condition_variable (条件变量),并没有 semaphore (信号量)。信号量,操作系统中一般都有提,linux下有实现。原生C++并没有提供,可以使用 mutex+condition_variable 实现一个.

信号量
神马是信号量?信号量是一个整数 count,提供两个原子(atomic,不可分割)操作:P 操作和 V 操作,或是说 wait 和 signal 操作。

P操作 (wait操作,又可以称为acquire操作,即获取一个资源):count 减1;如果 count <0 ,表示资源不足,那么挂起执行线程;
V操作 (signal操作,又可以称为release操作,即释放一个资源):count 加1;如果 count <=0,那么唤醒一个执行线程;
如何理解这个信号量?为嘛信号量是这个东西?想想互斥量 mutex,相当于一把锁,如果一个人来了 lock 一下,其他人进不去了;最初的人 unlock 了,又可以进一个人了,进去一个又 lock 住。
如果 mutex 锁在 unlock 状态下叫做 1 的话,lock 状态叫 0;1 实际反映的是锁的数量 !
现在可以有多把锁,数量 count 最初为 n (可以设定);
代码如下:

#include <condition_variable>
#include <mutex>

class Semaphore
{
public:
    Semaphore(int value = 1) :count(value) {}

    void Acquire()
    {
        unique_lock<mutex> lck(mtk);
        if (--count < 0)//资源不足挂起线程
            cv.wait(lck);
    }

    void Release()
    {
        unique_lock<mutex> lck(mtk);
        if (++count <= 0)//有线程挂起,唤醒一个
            cv.notify_one();
    }

private:
    int count;
    mutex mtk;
    condition_variable cv;
};

使用方法:
Semaphore.Acquire();
todo
Semaphore.Release();

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 共享内存是一种进程间通信的机制,它允许多个进程访问同一块内存区域。通过将内存区域映射到多个进程的地址空间中,实现了数据的共享。这种方式可以提高进程间的通信效率,避免了复制数据的开销。 信号量也是一种进程间通信的方式,它可以用来协调多个进程对共享资源的访问。信号量可以用来控制进程的执行顺序,避免出现竞争条件和死锁。通过对信号量的操作(如等待、唤醒等),进程可以申请或释放共享资源,并保证资源的独占性。 在使用共享内存时,需要使用信号量来保证对内存区域的正确访问。例如,当一个进程需要修改共享内存中的数据时,它需要先获取一个信号量,并在操作完成后释放信号量,以防止其他进程同时访问同一块内存区域。 综合来看,共享内存和信号量是一种相互配合的进程间通信方式。共享内存提供了高效的数据共享机制,而信号量用于对共享资源的访问进行协调和控制。在实际应用中,可以使用这两种方式来实现进程间的数据共享和同步。 ### 回答2: 共享内存和信号量是进程间通信的两种常用方式。 共享内存是一种在多个进程之间共享内存区域的方式。多个进程可以通过映射到同一块内存区域的方式来同时访问数据,从而实现数据的共享和交互。使用共享内存可以提高进程间的数据传递效率,尤其适用于需要频繁读写大量数据的场景。但是由于多进程共享同一块内存,必须通过信号量等机制来协调不同进程对内存的访问,避免数据一致性问题。 信号量是一种用于进程间同步和互斥的机制。它可以保证多个进程按照一定顺序执行,并避免竞争条件导致的数据错误。信号量的基本概念是一个计数器,多个进程可以通过对信号量的操作来实现对共享资源的访问控制。当进程需要使用某个共享资源时,它首先会检查信号量的值,如果满足条件,就可以访问资源并将信号量的值减一;否则,进程将等待,直到有其他进程释放资源并增加信号量的值。使用信号量可以有效避免资源的竞争和死锁问题。 综上所述,共享内存和信号量是两种常用的进程间通信方式。共享内存用于实现多个进程之间的数据共享和交互,提高效率;而信号量用于实现进程之间的同步和互斥,保证资源的正确访问。在实际应用中,需要根据具体场景需要选择合适的通信方式。 ### 回答3: c 共享内存是一种进程间通信的机制,通过将一块内存区域映射到多个进程的地址空间中,实现多个进程之间的数据共享。共享内存可以提高进程间通信的效率,因为数据在进程间直接传递,不需要复制和拷贝的操作。 共享内存可以通过操作系统提供的相关函数来实现,比如在Linux中可以使用shmget、shmat等函数来创建和关联共享内存区域。多个进程可以在同一块共享内存区域中读写数据,进程之间进行协调和同步。 而信号量是一种用于进程间同步和互斥的机制,通过对共享资源的访问进行控制,防止多个进程同时对同一资源进行读写,避免竞争条件的发生。 信号量可以用于进程间的互斥操作,保证只有一个进程可以访问某个资源;也可以用于进行进程间的同步操作,当一个进程完成某个任务后,可以通知另一个等待的进程开始执行。 在操作系统中,信号量通过操作系统提供的相关函数进行实现,比如在Linux中可以使用semget、semop等函数来创建和操作信号量。 在实际应用中,通常会同时使用共享内存和信号量来完成进程间的通信和同步操作,共享内存提供数据共享的机制,而信号量提供对共享资源的控制和同步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值