semop操作中:sembuf结构的sem_flg成员可以为0、IPC_NOWAIT、SEM_UNDO 。为SEM_UNDO时,它将
使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统
将自动释放该进程持有的。
sembuf结构的sem_flg成员为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这
个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。防止其他进程因为得不到
信号量而 发生【死锁现象】。
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
static int sem_id = 0;
static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();
int main(int argc, char *argv[])
{
char message = 'X';
int i = 0;
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if(argc > 1)
{
if(!set_semvalue())
{
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
message = argv[1][0];
sleep(2);
}
for(i = 0; i < 10; ++i)
{
if(!semaphore_p())
exit(EXIT_FAILURE);
printf("%c", message);
fflush(stdout);
sleep(rand() % 3);
printf("%c", message);
fflush(stdout);
if(!semaphore_v())
exit(EXIT_FAILURE);
sleep(rand() % 2);
}
sleep(10);
printf("\n%d - finished\n", getpid());
if(argc > 1)
{
sleep(3);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
static int set_semvalue()
{
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
return 0;
return 1;
}
static void del_semvalue()
{
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore\n");
}
static int semaphore_p()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
return 0;
}
return 1;
}
static int semaphore_v()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;//V()
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_v failed\n");
return 0;
}
return 1;
}
SEM_UNDO是semun中的sem_flg成员的标志位,设定该标志位主要是用来异常退出进程时进行调整,操作系统所设置的调整如下:
由于信号量的生命周期是“随系统”,即如果我们创建了信号量集合,但是没有对其进行删除,那么信号量便会一直存在在操作系统中,直到操作系统关闭或者用户使用命令删除为止,而如果我们为信号量设定了SEM_UNDO标志,如果sem_op的值小于0(即此时调用进程占有临界区域),当该进程终止时,内核都会检验进程是否还有尚未处理的信号量调整值,如果有则进行相应的调整。
当操作信号量(semop)时,sem_flg可以设置SEM_UNDO标识;SEM_UNDO用于将修改的信号量值在进程正常退出(调用exit退出或main执行完)或异常退出(如段异常、除0异常、收到KILL信号等)时归还给信号量。
2、回滚
mywait用于对信号等进可回滚的P操作,可回滚的意思是,当对信号灯进行P操作的进程因为某些意外或者有意识的
退出(死亡)后,信号等将自动恢复到操作前的状态,例如某个进程因为做了P操作阻塞了,在阻塞过程中被人人为
的kill掉,该进程死后,系统讲自动的对该信号量做一次V操作以保持平衡,这样的好处是在进行互斥操作的多进程程
序不会因为某个进程只做P操作而未做V操作就退出后,发生死锁行为。
myuwait 是对信号灯进行不可回滚的P操作,使用起来要各位小心。否则容易发生死锁
需要注意的是多某个信号灯变量做可回滚的P操作,就必须使用可回滚的V操作,就是说如果对某个信号灯用mywait
操作,就必须使用mysignal做V操作,而不能使用myusignal,并且其他地方要对该信号灯变量做P,V操作,都必须使
用mywait 和 mysignal ,mywait 和 myuwait不能对同一个信号等变量混合使用,否则系统会发生不可预测的错
误。
三、我们在父进程中用fork创建子进程,然后父进程向屏幕上打印AA,子进程向屏幕上打印BB,观察在设置信号量和没有设置信号量时所出现的不同的情况,案例源代码如下: