Linux内核编程 信号量 进程锁机制

目录

一:IPC技术 信号量

二:pv操作

三:信号量的分类

四:信号量函数

五:信号量 进程锁机制


一:IPC技术 信号量

进程间通信,Linux内存三剑客包括有:消息队列、共享内存以及信号量

信号量用于解决进程数据共享的安全问题

共享内存的前后置服务器分离中使用到了信号量

Dijkstra提出的“信号量”概念是共发程序(两/多个进程同时在访问同一个数据)设计领域的一项重大进步

信号量是一种变量,它只能取正整数值,对这些正整数只能进行两种操作:等待和信号

用两种记号来表示信号量的这两种操作:

P(semaphore variable) 代表等待  -1

V(semaphore variable) 代表信号  +1

线程:多条线程同时访问同一个数据源,会出现线程同步问题(一个进程中的多条线程)

线程同步解决方案:线程互斥量、线程信号量(使用线程锁解决进程内部的问题)

进程:多条进程同时访问同一个数据源,会出现进程共发程序设计问题(不同/多个进程中的多条线程)(进程包含线程,进程至少都会有一个主线程)

进程数据共享的安全问题解决方案:IPC信号量(进程锁解决进程外部问题

二:pv操作

假设我们有一个信号量变量sv,则pv操作的

定义如下

P(sv):如果sv的值大于零,就给它减去1;

             如果sv的值等于零,就挂起(阻塞)该进程的执行【P操作加锁

V(sv):   如果有其他进程因等待sv变量而被挂起,就让它恢复执行;

             如果没有进程因等待sv变量而被挂起,就给它加1【V操作解锁

三:信号量的分类

最简单的信号量是一个只能取“0”和“1”值的变量,也就是人们常说的“二进制信号量”(用做进程锁)

可以取多种正整数值的信号量叫做“通用信号量”(进程间通信做通知,但是信号量、消息队列相对信号量而言更好用)

四:信号量函数

消息队列:msgget(创建/访问)  msgctl(删除)    msgsnd(发送)         msgrcv(接收)

共享内存:shmget(创建/访问)  shmctl(删除)    shmat(连接)            shmdt(断开)

信号量:    semget(创建/访问)  semctl(删除)    semop(P和V操作)

每一个信号量函数都能对成组的通用信号量进行操作,自然也可以完成对最简单的二进制信号量的操作

还经常需要用到头文件<sys/types.h>和<sys/ipc.h>

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

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

int semop(int sem_id,struct sembuf * sops,size_t nsops);

函数名:

semget函数

函数作用:

创建一个新的信号量或者取得一个现有信号量的键字 

函数原型:

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

函数参数:

key:是一个整数值,不相关的进程将通过这个值去访问同一个  信号量

num_sems:需要使用的信号量个数,它几乎总是取值为1

sem_flags:是一组标志,其作用与open函数的各种标志很相似,它低端的九个位是该信号量的权限,其作用相当于文件的访问权限,可以与键值IPC_CREATE做按位的OR操作以创建一个新的信号量(IPC_CREAT|0766)

函数返回:成功时将返回一个正数值,它就是其他信号量函数要用到的那个标识码,如果失败,将返回-1

函数名:

semop函数

函数作用:

改变信号量的键值

函数原型:

int  semop ( int sem_id,  struct sembuf *sem_ops,size_t num_sem_ops,);

函数参数:

sem_id:是该信号量的标识码,也就是semget函数的返回值

sem_ops:是个指向一个结构数值的指针

Semop调用的一切动作都是一次性完成的,这是为了避免出现因使用了多个信号量而可能发生的竞争现象

其中的

sembuf结构体中的元素

  struct sembuf{

  short sem_num;

  short sem_op;

  short sem_flg;

  };

sem_num是信号量的编号,如果你的工作不需要使用一组信号量,这个值一般就取为0

sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用

sem_flag通常被设置为SEM_UNDO;它将使操作系统跟踪当前进程对该信号量的修改情况

函数名:

semctl函数

函数作用:

允许我们直接控制信号量的信息

函数原型:

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

函数参数:

sem_id:是由semget函数返回的一个信号量标识码

sem_num:信号量的编号,如果在工作中需要使用到成组的信号量,就要用到这个编号;它一般取值为0,表示这是第一个也是唯一的信号量

comman:将要采取的操作动作

如果还有第四个参数,那它将是一个“union semun”复合结构

删除 semctl(sem_id,0,IPC_RMID);

semctl函数里的command可以有许多不同的值,下面这两个是比较常用的:

SETVAL:用来把信号量初始化为一个已知的值,这个值在semun结构里是以val成员的面目传递的

IPC_RMID:删除一个已经没有人继续使用的信号量标识码

五:信号量 进程锁机制

下面创建两个工程

分别代表两个进程

#include<iostream>
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<unistd.h>

using namespace std;

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


//信号量的创建
int sem_create(key_t key, int num_sems)
{
	int res = 0;
	res = semget(key,num_sems, IPC_CREAT | 0777);
	if (res < 0)
	{
		perror("semget error");
	}
	return res;
}

//信号量赋初始值
int sem_setVal(int semid, int semindex, int val)
{
	union semun arg;
	arg.val = val;

	int res = semctl(semid, semindex, SETVAL, arg);
	if (res < 0)
	{
		perror("semctl error");
	}
	return res;
}

//信号量 P操作 -1
int sem_p(int sem_id, int semindex)
{
	struct sembuf buf = { semindex,-1,SEM_UNDO };
	int res = semop(sem_id, &buf, 1);
	if (res < 0)
	{
		perror("semop error");
	}
	return res;
}

//信号量 V操作 +1
int sem_v(int sem_id, int semindex)
{
	struct sembuf buf = { semindex,1,SEM_UNDO };
	int res = semop(sem_id, &buf, 1);
	if (res < 0)
	{
		perror("semop error");
	}
	return res;
}

int main()
{
	//信号量创建
	int semid = sem_create((key_t)1003, 1);
	//信号量0下标设置初始值1
	sem_setVal(semid, 0, 1);
	//加锁
	sem_p(semid, 0);
	for (int i = 0; i < 30; i++)
	{
		cout << "第一个工程进程----------" << i << endl;
		sleep(1);
	}
	//解锁
	sem_v(semid, 0);
	return 0;
}

#include<iostream>
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<unistd.h>

using namespace std;

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


//信号量的创建
int sem_create(key_t key, int num_sems)
{
	int res = 0;
	res = semget(key, num_sems, IPC_CREAT | 0777);
	if (res < 0)
	{
		perror("semget error");
	}
	return res;
}


//信号量 P操作 -1
int sem_p(int sem_id, int semindex)
{
	struct sembuf buf = { semindex,-1,SEM_UNDO };
	int res = semop(sem_id, &buf, 1);
	if (res < 0)
	{
		perror("semop error");
	}
	return res;
}

//信号量 V操作 +1
int sem_v(int sem_id, int semindex)
{
	struct sembuf buf = { semindex,1,SEM_UNDO };
	int res = semop(sem_id, &buf, 1);
	if (res < 0)
	{
		perror("semop error");
	}
	return res;
}

int main()
{
	//信号量访问
	int semid = sem_create((key_t)1003, 1);

	//加锁
	sem_p(semid, 0);

	for (int i = 0; i < 30; i++)
	{
		cout <<"--------------第二个工程进程----------" << i << endl;
		sleep(1);
	}
	//解锁
	sem_v(semid, 0);

	return 0;
}

测试结果:

只有在第一个进程执行结束之后,第二个进程才可以执行启动

ipcs查看内存三剑客,不难看出

两个进程在运行的时候,信号量是一直存在的(信号量由操作系统直接管理)

等待两个进程运行结束,

可以通过 ipcrm -a (初始化内存三剑客)  来删除信号量

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenruhan_QAQ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值