同步与互斥
同步发生在不同调度单位中(线程/进程),用来保证调度单位的时序性,比如生产者消费者模型中,消费者必须在生产者产出后才能消费,它们是存在先后顺序的。
互斥用来发生在同一时间只能有一个调度单位去临界区(临界区指一段公共代码)执行相应逻辑,比如在多线程下的消费者生产者模型下,多个消费者消费的时候,需要互斥,因为消费这个动作不是原子的,所以可能俩个线程消费一个资源。
Linux下的同步与互斥
- 互斥锁(pthread_mutex)
- 条件变量(pthread_condition)
- 信号量(sem)
- 读写锁
- 自旋锁
- 大内核锁
信号量
semget
int semget(key_t key ,int nsems , int semflg)
key_t 标识ipc资源的一个键值
nsems: 信号集中信号量的个数
semflg:权限位与创建文件基本一致(664,666)
它用来创建一个信号量集,返回值大于0的时候,返回信号量集的标识符,
这个标识符后面的操作函数需要用到,失败返回-1。
semctl
int semctl( int semid , int semnum , int cmd , ...)
cmd :
SETVAL : 设置对应下标信号量的值
GETVAL : 得到信号量集中对应下标的信号量的值
SETALL : 设置数组初始化
GETALL : 将信号量集内容copy数组中。
IPC_STAT : 将semid_ds中的数据 初始化为 当前信号集的当前值
IPC_SET : 在权限的前提下,把信号量集的当前关联值设置为 semid_ds中给出的值
IPC_RMID :删除当前信号量集
最后一个可变参数是
union semun{
int val; SETVAL的时候用
struct semid_ds * buf; IPC_STAT/SET 使用
unsigned short array; SETALL的时候用
struct seminfo * _buf; IPC_INFO的时候用
}
成功返回0 , 失败返回-1.
1.初始化信号量集的时候用该函数
2.信号量是POSIX的进程间通信的一种方式,所以随内核,如果我们不释放造成资源泄露。
semop
int semop( int semid, struct sembuf [] sops , unsigned nsops);
semid:前面讲的标识符
sops : sembuf 数组
nsops : 数组大小
成功返回0 失败-1
struct sembuf {
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
}
sem_flg : SEM_UNDO 可以防止死锁在进程退出的时候,防止没有规划信号量计数
SEM_UNWAIT 当信号量为0的时候,P操作时返回 EAGIAN 不阻塞。
这个函数用来PV操作的,P申请资源对对应信号量减1 , V释放资源对相应信号量加1。
二元信号量(相当于互斥锁)代码
COMM.H
#ifndef _COMM_H
#define _COMM_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/wait.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
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 createSemSet( int nums );
int initSem ( int semid , int nums , int initVal);
int getSemSet ( int nums);
int P (int semid , int who);
int V (int semid , int who);
int destroySemSet ( int semid);
#endif
COMM.CC
#include "comm.h"
static int commSemSet ( int nums , int flags )
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0){
¦ perror("ftok");
¦ return -1;
}
int semid = semget(key , nums , flags);
if(semid < 0){
¦ perror("semget");
¦ return -2;
}
return semid;
}
int createSemSet ( int nums )
{
return commSemSet ( nums , IPC_CREAT|IPC_EXCL|0666);
}
int getSemSet ( int nums )
{
return commSemSet ( nums , IPC_CREAT);
}
int initSem(int semid , int nums , int initVal)
{
union semun _un;
_un.val = initVal;
if(semctl(semid , nums , SETVAL ,_un)==-1)
{
¦ perror("semctl");
¦ return -4;
}
return 0;
}
static int commPV( int semid ,int who ,int op)
{
¦struct sembuf _buf;
¦_buf.sem_num = who;
¦_buf.sem_op = op;
¦_buf.sem_flg = SEM_UNDO;
¦if(semop(semid , &_buf , 1)==-1)
{
¦ perror("semop");
¦ return -5;
}
¦return 0;
}
int P ( int semid , int who)
{
return commPV(semid,who,-1);
}
int V ( int semid , int who)
{
return commPV(semid,who,1);
}
int destroySemSet(int semid)
{
if(semctl(semid,0,IPC_RMID) < 0){
¦ perror("semctl");
¦ return -6;
}
return 0;
}
main.cc
#include "comm.h"
int main()
{
int semid = createSemSet(1);
initSem(semid , 0 ,1);
pid_t id = fork();
if(id == 0)
{
¦ int _semid = getSemSet(0);
¦ while(1)
¦ {
¦ ¦ P(_semid,0);
¦ ¦ printf("A");
¦ ¦ fflush(stdout);
¦ ¦ usleep (100000);
¦ ¦ printf("A");
¦ ¦ fflush(stdout);
¦ ¦ usleep (100000);
¦ ¦ V (_semid , 0 );
¦ }
}
else
{
¦ while(1)
¦ {
¦ ¦ P (semid , 0);
¦ ¦ printf("B");
¦ ¦ fflush(stdout);
¦ ¦ usleep (100000);
¦ ¦ printf("B");
¦ ¦ fflush(stdout);
¦ ¦ usleep (100000);
¦ ¦ V(semid , 0);
¦ }
¦ waitpid(id,NULL,-1);
}
destroySemSet(semid);
return 0;
}