对于不同进程间的通信可以通过使用前面介绍过得管道来实现,管道类似一种文件系统,但是只能单向进行数据传送。使用不是很方便。今天通过使用信号量,可以很方便的完成两个进程间的交互。多与不同的进程访问同一块区域,通过使用信号量,确保同一时刻只有获得信号量的进程可以访问,当访问进程释放信号量,其它进程竞争获取信号量,得到信号量的进程可以进行访问。
二、信号量的工作原理
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。
函数原型:int semget(key_t key,int nsems,int semflg);
功能描述
获取与某个键关联的信号量集标识。信号量集被建立的情况有两种:
1.如果键的值是IPC_PRIVATE。
2.或者键的值不是IPC_PRIVATE,并且键所对应的信号量集不存在,同时标志中指定IPC_CREAT。
当调用semget创建一个信号量时,他的相应的semid_ds结构被初始化。ipc_perm中各个量被设置为相应
值:
该函数用来直接控制信号量信息,它的原型为:
int semctl(int sem_id, int sem_num, int command, ...);
如果有第四个参数,它通常是一个union semum结构,定义如下:
union semun{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:struct sembuf{
short sem_num;//除非使用一组信号量,否则它为0
short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
#include
#include
#include
#include
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
//初始化信号量
static int set_semvalue(void);
//删除信号量
static void del_semvalue(void);
//获得信号量
static int semaphore_p(void);
//释放信号量
static int semaphore_v(void);
static int sem_id;
int main(int argc,char **argv)
{
int i;
int stop_time;
char c = 'o';
//创建信号量
sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
if(argc > 1)
{
//初始化信号量
if(!set_semvalue())
{
fprintf(stderr,"Failed to initialize semaphore\n");
exit(-1);
}
c = 'x';
sleep(2);
}
for(int i=0; i< 10; i++)
{
//获得排它锁
if(!semaphore_p())
{
printf("semp p failed\n");
exit(-1);
}
printf("%c",c);
fflush(stdout);
stop_time = rand() % 2;
sleep(stop_time);
printf("%c",c);
fflush(stdout);
//释放排它锁
if(!semaphore_v())
{
printf("failed to set v \n");
exit(-1);
}
stop_time = rand() % 2;
sleep(stop_time);
}
printf("%d has finilsshed\n",getpid());
if(argc > 1)
{
sleep(5);
//删除信号量
del_semvalue();
}
exit(0);
}
static int set_semvalue(void)
{
union semun sem_unition;
sem_unition.val = 1;
//初始化信号量,设置信号量值为1
if(semctl(sem_id,0,SETVAL,sem_unition) == -1)
{
printf("faile to semctl\n");
return -1;
}
return 1;
}
static void del_semvalue(void )
{
union semun sem_unition;
//删除信号量
if(semctl(sem_id,0,IPC_RMID,sem_unition) == -1)
{
printf("failed to semctl\n");
return;
}
}
static int semaphore_p(void )
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
//获得信号量 ,并进行减1操作,信号量为0之后挂起。禁止其它进程访问
if(-1 == semop(sem_id,&sem_b,1))
{
printf("semaphore failed\n");
return 0;
}
return 1;
}
static int semaphore_v(void )
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
//释放信号量 ,并进行加1操作,信号量大于0之后执行。其它进程可以访问
if(-1 == semop(sem_id,&sem_b,1))
{
printf("semaphore failed\n");
return 0;
}
return 1;
}
administrator@ubuntu:~/桌面/Java/process$ ./singnalprocess 1 &
[2] 2955
[1] 完成 ./singnalprocess 1
administrator@ubuntu:~/桌面/Java/process$ ./singnalprocess
ooxxooxxooxxooooxxxxooooxxxxooxxooxxooxx2956 has finilsshed
2955 has finilsshed
administrator@ubuntu:~/桌面/Java/process$