//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\n");
}
P(a->semid, 0, 1);
if(momney < 0 || a->balance < momney)
{
printf("momney not enough\n");
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\n");
}
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\n");
}
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\n",a->code, money);
printf("get balance = %f\n",get_balance(a));
shmdt(a);
}else{//father process
double money = get_momney(a, 10000);
printf("father get %d from %f\n",a->code, money);
printf("get balance = %f\n",get_balance(a));
wait(NULL);
D(a->semid);
shmdt(a);
shmctl(shmid, IPC_RMID,NULL);
}
return 0;
}