【简介】
信号量用于解决2个或者多个进程访问共享资源的问题。
几个主要函数:
(1)int semget(key_t key,int nsems,int flag);
信号量和消息队列、共享内存都是需要先通过ftok获得一个key,再用对应的函数进行创建,返回IPC标识符。nsems表示信号量集合中的信号个数,flag为可选参数。函数执行失败返回负1.
(2)int semctl(int semid,int semnum,int cmd,union semun *arg);
该函数用于在一组信号量上做各种控制操作:比如信号集合初始化,状态查询等等。信号量创建时初值不确定,可以使用SETVAL或者SETALL来创建,区别在于如果cmd为SETALL,semnum为信号量集合中的信号总数;如果为SETVAL,semnum为信号量元素编号。
(3)int semop(int semid,struct sembuf semoparray[], int nops);
用于对信号量集合中的一个或者多个信号进行操作,操作命令由用户提供的结构体决定,结构体定义如下:
struct sembuf{
short sem_num;
short sem_op;
short sem_flg;
};
sem_flg如果为SEM_UNDO,表示如果进程执行的信号量操作锁住了某些资源,而没有释放就返回了,那么进程退出以后,由系统撤销进程做过操作对信号量的影响。
【代码】
函数功能:由命令行参数决定创建子进程个数,每个子进程要访问2次临界资源才可以退出。
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
//semctl()的arg结构体
union semun{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int main(int argc,char* argv[])
{
int num;
int sem_id;
pid_t pid;
int i,j;
int prj_id = 3;
key_t key;
union semun arg;
//-1表示要申请资源,1表示要释放资源
static struct sembuf acquire = {0,-1,SEM_UNDO};
static struct sembuf release = {0,1,SEM_UNDO};
//需要带一个参数执行,表示创建子进程个数
if(argc != 2)
{
printf("Usage: %s child_process_num.\n",argv[0]);
return 1;
}
num = atoi(argv[1]);
//首先也必须通过ftok获得一个key,才可以用于信号量标识符的创建,创建以后,其他进程可以用这个key访问信号量,获得sem_id
key = ftok("/home/gaolu",prj_id);
if(-1 == key)
{
perror("Can't generate the IPC key.\n");
return 1;
}
sem_id = semget(key,1,IPC_CREAT|IPC_EXCL|0666);
if(-1 == sem_id)
{
perror("Can't creat semaphore set.\n");
return 1;
}
static unsigned short init_value = 1;
arg.array = 1;
if(semctl(sem_id,0,SETVAL,arg)==-1)
{
perror("Can't set semaphore set.\n");
return 1;
}
for(i=0;i<num;i++)
{
pid = fork();
if(-1 == pid)
{
perror("Fail to creat child process.\n");
return 1;
}
else if(pid == 0)
{
sem_id = semget(key,1,0); //在子进程中获得sem_id
if(sem_id == -1)
{
perror("Child can't get access right.\n");
_exit(1);
}
for(j=0;j<2;j++)
{
sleep(i);
if(semop(sem_id,&acquire,1)==-1)
{
perror("Can't acquire resource.\n");
_exit(1);
}
printf("===Enter the critical section===\n");
printf("---pid: %ld---\n",(long)getpid());
sleep(1);
printf("===Leave the critical section===\n");
printf("\n\n");
if(semop(sem_id,&release,1)==-1)
{
perror("Can't release resource.\n");
_exit(1);
}
}
_exit(0);
}
}
for(i=0;i<num;i++)
{
wait(NULL);
}
if(semctl(sem_id,0,IPC_RMID,0)==-1)
{
perror("Can't delete the semaphore set.\n");
return 1;
}
return 0;
}
执行结果:
gaolu@gaolu-desktop:~$ gcc -o sema systemcall2.c
systemcall2.c:48: warning: assignment makes pointer from integer without a cast
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ ./sema 5 //结果是比较随机滴
===Enter the critical section===
---pid: 5862---
===Leave the critical section===
===Enter the critical section===
---pid: 5863---
===Leave the critical section===
===Enter the critical section===
---pid: 5862---
===Leave the critical section===
===Enter the critical section===
---pid: 5864---
===Leave the critical section===
===Enter the critical section===
---pid: 5865---
===Leave the critical section===
===Enter the critical section===
---pid: 5863---
===Leave the critical section===
===Enter the critical section===
---pid: 5866---
===Leave the critical section===
===Enter the critical section===
---pid: 5864---
===Leave the critical section===
===Enter the critical section===
---pid: 5865---
===Leave the critical section===
===Enter the critical section===
---pid: 5866---
===Leave the critical section===
gaolu@gaolu-desktop:~$