信号量 --- sem_lock

信号量sem  -----  负责进程间 互斥、同步 等功能 ---- 计量某种资源的个数

1、  本质是一种 数据操作锁(计数器),它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。

信号量以 信号量集 申请资源。

临界资源:多个进程能够访问的资源

临界区  :访问临界资源的一段代码

互斥 :独占临界资源

同步 :带着顺序性的进程运行,(大部分)建立在互斥的情况下

P操作:检查信号量,不为0 则 信号量-1;反之(为0),挂起  --- 申请资源

V操作:检查信号量,为0 则 信号量+1 并 唤醒,进入临界区;反之(!=0),挂起

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

信号量的 P、V操作及 增减 可以保证 原子性,而其 创建和初始化,则不能保证。

2、 联合 和 结构体:

union semun 
{
    int val; // 使用的值
    struct semid_ds *buf; // IPC_STAT、IPC_SET 使用缓存区
    unsigned short *array; // GETALL,、SETALL 使用的数组
    struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区
};
struct sembuf
{
      unsigned short sem_num;  /* semaphore number */
      short sem_op;            /* semaphore operation */
      short sem_flg;           /* operation flags */
};

3、SEM_UNDO :

程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

(1)semop操作: 它基于每个具体指定的sem_op操作的三个可能值:正数,负数或0。

1. 如果sem_op是正数,其值就加到semval上,这对应于释放由某个信号量控制的资源。

如果指定了SEM_UNDO标志,那就从相应信号灯的semadj值中减掉sem_op的值。

2. 如果sem_op是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值。这对对应于分配资源。

如果semval大于或等于sem_op的绝对值,那就从semval中减掉sem_op的绝对值。

如果指定了SEM_UNDO标志,那么sem_op的绝对值就加到相应信号灯的semadj值上。

3. 如果sem_op等于0,则程序必须具有 读 权限,等待进程直至有进程发生。

(2)使用注意事项:

    • 使用SEM_UNDO标志会在semadj中记录对信号量进行的操作,如果程序退出时会将semadj的值加到semval上。
    • SEM_UNDO需要成对使用,如果不是成对的使用semadj,可能会导致semadj溢出而发生错误。
    • 程序启动时需要(重新)初始化所有资源(信号量等),退出时需要清理各种资源(信号量/共享内存等)。

4、实例:信号锁

//comm.h

#pragma once

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

#define _PATH_ ‘.‘
#define _PROJ_ID_ 0x6666

union semun
{
	int val; // 使的值
	struct semid_ds *buf; // IPC_STAT、IPC_SET 使用缓存区
	unsigned short *array; // GETALL,、SETALL 使用的数组
	struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区
};

static int comm_sem_set(int _sem_num,int flags);
int create_sem_set(int _sem_num);
int get_sem_set(int _sem_num);
int init_sem_set(int _sem_id,int _seq_num,int _init_val);
static int comm_sem_op(int _sem_id,int _seq_num,int _op);
int p_sem_elem(int _sem_id,int _seq_num);
int v_sem_elem(int _sem_id,int _seq_num);
int destroy_sem_set(int _sem_id);

//comm.c

#include "comm.h"

static int comm_sem_set(int _sem_num,int flags)
{
	key_t _key=ftok((char*)_PATH_,_PROJ_ID_);
	if(_key < 0)
	{
		perror("ftok");
		return -1;
	}
	int sem_id=semget(_key,_sem_num,flags);
	if(sem_id < 0)
	{
		perror("semget");
		return -1;
	}
	return sem_id;
}
int create_sem_set(int _sem_num)
{
	int flags=IPC_CREAT|IPC_EXCL|0666;
	return comm_sem_set(_sem_num,flags);
}
int get_sem_set(int _sem_nums)
{
	int flags=IPC_CREAT;
	return comm_sem_set(_sem_nums,flags);
}
int init_sem_set(int _sem_id,int _seq_num,int _init_val)
{
	union semun _un;
	_un.val=_init_val;
	if(semctl(_sem_id,_seq_num,SETVAL,_un) < 0)
	{
		perror("semctl");
		return -1;
	}
	return 0;
}
static int comm_sem_op(int _sem_id,int _seq_num,int _op)
{
	struct sembuf _sem_buf[1];
	_sem_buf[0].sem_num=_seq_num;
	_sem_buf[0].sem_op=_op;
	if(semop(_sem_id,_sem_buf,1) < 0)
	{
		perror("semop");
		return -1;
	}
	return 0;
}
int p_sem_elem(int _sem_id,int _seq_num)
{
	return comm_sem_op(_sem_id,_seq_num,-1);
}
int v_sem_elem(int _sem_id,int _seq_num)
{
	return comm_sem_op(_sem_id,_seq_num,1);
}
int destroy_sem_set(int _sem_id)
{
	if(semctl(_sem_id,0,IPC_RMID,NULL) < 0)
	{
		perror("semctl");
		return -1;
	}
	return 0;
}

(1)未加锁前 --->

//sem_lock.c

#include "comm.h"

int main()
{
	pid_t id=fork();
	if(id < 0)
	{
		perror("fork");
		return -1;
	}
	else if(id == 0)//Child
	{
		while(1)
		{
			printf("A");
			sleep(1);
			fflush(stdout);

			printf("A");
			sleep(3);
			fflush(stdout);
		}
	}
	else//Father
	{
		while(1)
		{
		        printf("B");
			sleep(1);
			fflush(stdout);

			printf("B");
			sleep(2);
			fflush(stdout);
		}
		waitpid(id,NULL,0);
	}
	return 0;
}

运行结果:

(2)加锁后 --->

//sem_lock.c

#include "comm.h"

int main()
{
	int sem_id=create_sem_set(1);
	init_sem_set(sem_id,0,1);
	pid_t id=fork();
	if(id < 0)
	{
		perror("fork");
		return -1;
	}
	else if(id == 0)//Child
	{
		int _sem_id=get_sem_set(1);
		while(1)
		{
			p_sem_elem(_sem_id,0);

			printf("A");
			sleep(1);
			fflush(stdout);

			printf("A");
			sleep(3);
			fflush(stdout);

			v_sem_elem(_sem_id,0);
		}
	}
	else//Father
	{
		int _sem_id=get_sem_set(1);
		while(1)
		{
			p_sem_elem(_sem_id,0);

			printf("B");
			sleep(1);
			fflush(stdout);

			printf("B");
			sleep(2);
			fflush(stdout);

			v_sem_elem(_sem_id,0);
		}
		waitpid(id,NULL,0);
	}
	destroy_sem_set(sem_id);
	return 0;
}

运行结果:

时间: 2024-10-06 00:40:25

信号量 --- sem_lock的相关文章

了解信号量Semaphore和线程池的差异

信号量 其实本质上是锁,Lock是单锁,信号量是指定多把锁,也就是说通过信号量指定多个数线程可以访问相同资源,一般情况下读操作可以有多个,但写操作同时只有一个 信号量模块 semaphore # 使用起来和普通锁没 什么区别,但这个是比锁更加粗粒度锁,锁的是线程 # 在线程实例前加锁,把锁传递进线程,在线程结束时候释放锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from threading import Thread

Linux程序设计学习笔记----System V进程间通信(信号量)

关于System V Unix System V,是Unix操作系统众多版本中的一支.它最初由AT&T开发,在1983年第一次发布,因此也被称为AT&T System V.一共发行了4个System V的主要版本:版本1.2.3和4.System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如"SysV 初始化脚本"(/etc/init.d),用来控制系统启动和关闭,System V Interface Definitio

信号量与并发控制

代码1 - 未使用信号量控制并发: #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSUInt

Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3534050.html Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可

同步原语之信号量

一.semaphore信号量分析 首先,说明信号量的作用,信号量的作用类似于自旋锁(其实,同步原语均有相似之处),相似之处在于,都有临界区,各进程需互斥访问,linux中提供了两种信号量的实现,有内核态的和用户态的,这里只介绍内核态的 相关数据结构 1 struct semaphore { 2 spinlock_t lock; 3 unsigned int count; 4 struct list_head wait_list; 5 }; 1 struct mutex { 2 /* 1: unl

线程同步(条件变量、信号量)以及死锁

死锁:指两个或两个以上进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待现象,若无外力作用,它们都将无法继续推进下去. 例:交叉死锁:线程1获得了锁1,线程2获得了锁2,此时线程1调用lock想获得锁2,需挂起等待线程2释放锁2,而线程2也想获得锁1,也需挂起等待线程1释放锁1,此时两个线程都挂起等待 产生死锁的四个必要条件: (1):互斥条件(一个资源每次只能被一个进程或线程使用) (2):请求与保持条件(一个进程或线程因请求资源而阻塞时,对已获得的资源不释放) (3):不剥夺条件(此

信号量 (线程互斥)

信号量:表示可用资源的数量,MUTEX是非0即为1的, 即,如果信号量描述的资源数目是1时,此时的信号量和互斥锁相同! 调用sem_wait()可以获得资源(P操作),使semaphore的值减1,如果调用sem_wait()时 semaphore的值已经是0,则挂起等待.如果不希望挂起等待,可以调用sem_trywait() .调用 sem_post() 可以释放资源(V操作),使semaphore 的值加1,同时唤醒挂起等待的线程. 1.生产者不会把消费者套个圈 2.消费者不会超过生产者  

秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题 在<秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量>中对经典多线程同步互斥问题进行了回顾和总结,这篇文章对Windows系统下常用的线程同步互斥机制——关键段.事件.互斥量.信号量进行了总结.有网友问到互斥量能处理“遗弃”问题,事件和信号量是否也能处理“遗弃”问题.因此本文将对事件和信号量作个试验,看看事件和信号量能否处理“遗弃”问题. 一.

semget 信号量创建

Linux进程间通信(六)---信号量通信之semget().semctl().semop()及其用法:http://www.educity.cn/linux/1241661.html 信号量 Linux函数 semget();semctl();semop();:http://blog.csdn.net/ta893115871/article/details/7505560 信号量函数 semget() semop() semctl():http://blog.chinaunix.net/uid