# 信号量实例—互斥

```//account.h

#ifndef     _ACCOUNT_H
#define     _ACCOUNT_H

typedef struct{
int code;
double balance;
//定义一把互斥锁，用来对多线程操作的银行账户（共享资源）进行加锁（保护）的

/* 建议一把互斥锁和一个共享资源（银行账户）绑定，尽量不要设置成全局变量，否则可能出现
一把互斥锁去锁几百个账户（即一个线程获得锁，其他线程将阻塞），导致并发性的降低
*/
int semid;
}Account;

//取款
extern double get_momney(Account *a, double momney);

//存款
extern double save_momney(Account *a, double momney);

//获得余额
extern double get_balance(Account *a);

#endif```
```//account.c

#include "account.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "pv.h"

//取款
extern double get_momney(Account *a, double momney)
{
if(a == NULL){
printf("get_momney failed
");
}
P(a->semid, 0, 1);
if(momney < 0 || a->balance < momney)
{
printf("momney not enough
");
V(a->semid, 0, 1);
return 0.0;
}

double balance = a->balance;
sleep(1);
balance = balance - momney;
a->balance = balance;
V(a->semid, 0, 1);
return momney;
}

//存款
extern double save_momney(Account *a, double momney)
{
if(a == NULL){
printf("save_momney failed
");
}
P(a->semid, 0, 1);
if(momney < 0){
V(a->semid, 0, 1);
return 0.0;
}

double balance = a->balance;
sleep(1);
balance = balance + momney;
a->balance = balance;
V(a->semid, 0, 1);
return momney;
}

//获得余额
extern double get_balance(Account *a)
{
if(a == NULL){
printf("get_balance failed
");
}
P(a->semid, 0, 1);
double balance = a->balance;
V(a->semid, 0, 1);
return balance;
}```
```//pv.h

#ifndef _PV_H_
#define _PV_H_
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

//int semget(key_t key, int nsems, int semflg)
//初始化信号量集
extern int I(int nsems, int value);

//P操作
extern void P(int semid, int semno, int value);

//V操作
extern void V(int semid, int semno, int value);

//删除信号量集
extern void D(int semid);

#endif```
```//pv.c

#include "pv.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>

union semun{//根据cmd来确定联合体中的值，只能有一个
int val;
struct semid_ds *buf;
unsigned short *array;
};

//int semget(key_t key, int nsems, int semflg)
//创建并初始化信号量集
int I(int nsems, int value)
{
key_t key = ftok(".", 0);
int semid = semget(key, nsems, IPC_CREAT|IPC_EXCL|0777);//nsems:表示信号量的个数
if(semid < 0){
perror("semget failed");
return -1;
}

//int semctl(int semid,int semnum,int cmd,.../*union semun arg*/)
unsigned short array[nsems];
union semun u;
for(int i = 0; i < nsems; i++){
array[i] = value;
}
u.array = array;//信号量的初值
if(semctl(semid, 0, SETALL, u) < 0){//0：表示对信号量集中的所有信号 SETALL:表示对信号量集中的所有信号量赋初值
perror("semctl failed");
return -1;
}
return semid;
}

//P操作,对信号量集中的第semno的信号量作p操作，semno:0表示第一个信号量
void P(int semid, int semno, int value)
{
//int semop(int semid,struct sembuf *sops,size_t nsops)
/*struct sembuf{
unsigned short sem_num;//信号量集中信号量的编号（即哪个信号量）
short sem_op;//正数为V操作（加操作，1为加1操作，2为加2操作），负数为P操作（减操作，-1为减1操作，-2为减2操作）
short sem_flg;
} */
struct sembuf sb[] = {{semno, -value, SEM_UNDO}};//semno:0表示第一个信号量
if(semop(semid, sb, sizeof(sb)/sizeof(struct sembuf)) < 0){
perror("semopp failed");
}

}

//V操作
void V(int semid, int semno, int value)
{
struct sembuf sb[] = {{semno, value, SEM_UNDO}};
if(semop(semid, sb, sizeof(sb)/sizeof(struct sembuf)) < 0){
perror("semopv failed");
}

}

//删除信号量集
void D(int semid)
{
if(semctl(semid, 0, IPC_RMID, NULL) < 0){
perror("semctl failed");
}
}```
```//account_test.c

#include <stdio.h>
#include "account.h"
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "pv.h"
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
pid_t pid ;

key_t key = ftok("/etc/passwd",1);
int shmid = shmget(key, sizeof(Account), IPC_CREAT|IPC_EXCL|0777);

Account *a = (Account *)shmat(shmid, NULL, 0);
a->code = 1001;
a->balance = 10000.0;
a->semid = I(1, 1);

pid = fork();
if(pid < 0){
perror("creat fork");
}else if(pid == 0){//child  process
double money = get_momney(a, 10000);
printf("child get %d from %f
",a->code, money);
printf("get balance = %f
",get_balance(a));
shmdt(a);
}else{//father process
double money = get_momney(a, 10000);
printf("father get %d from %f
",a->code, money);
printf("get balance = %f
",get_balance(a));
wait(NULL);
D(a->semid);
shmdt(a);
shmctl(shmid, IPC_RMID,NULL);
}

return 0;
}```