semop操作中:sembuf结构的sem_flg成员可以为0、IPC_NOWAIT、SEM_UNDO 。为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。除非你对信号量的行为有特殊的要求,否则应该养成设置sem_flg为SEM_UNDO的好习惯。
1: //假设两个进程(父子进程)对一个文件进行写操作,但是这个文件同一时间只能有一个进程进行写操作。
2: //利用信号量实现pv操作
3: #include
4: #include
5: #include
6: #include
7: #include
8: #include
9: struct sembuf sops;
10: static int sid;
11: //创建一个新的信号量集
12: int createSemset(void)
13: {
14: char* pathname="semset";
15: if( access(pathname, F_OK)!=0 )
16: {
17: int fd=open(pathname, O_RDWR | O_CREAT, 0666);
18: if( fd<0 )
19: {
20: perror("open");
21: return -1;
22: }
23: }
24: key_t key=ftok(pathname, 'a');
25: if( -1==key )
26: {
27: perror("ftok");
28: return -1;
29: }
30: return semget(key, 1, IPC_CREAT | 0666) ;
31: }
32:
33: //P操作
34: int P(void)
35: {
36: sops.sem_num=0;
37: sops.sem_op=-1;
38: sops.sem_flg=0;
39: return semop(sid, &sops, 1);
40: }
41: //V操作
42: int V(void)
43: {
44: sops.sem_num=0;
45: sops.sem_op=1;
46: sops.sem_flg=0;
47: return semop(sid, &sops, 1);
48: }
49: int main(int argc, char *argv[])
50: {
51: sid=createSemset();
52: if( -1==sid )
53: {
54: perror("createSemset");
55: exit(1);
56: }
57:
58: if( -1==semctl(sid, 0, SETVAL, 1) )
59: {
60: perror("SETVAL");
61: exit(1);
62: }
63: pid_t pid=fork();
64: if( pid<0 )
65: {
66: perror("fork");
67: exit(1);
68: }
69: else if( 0==pid )
70: {
71: while(1)
72: {
73: if( -1==P() )
74: {
75: printf("P操作失败!/n");
76: exit(1);
77: }
78: printf("子进程正在对文件进行写操作!/n");
79: sleep(1);
80: printf("子进程写操作完毕,释放资源!/n");
81: if( -1==V() )
82: {
83: printf("V操作失败!");
84: exit(1);
85: }
86: }
87: }
88: else
89: {
90: while(1)
91: {
92: if( -1==P() )
93: {
94: printf("P操作失败!/n");
95: exit(1);
96: }
97: printf("父进程进程正在对文件进行写操作!/n");
98: sleep(1);
99: printf("父进程写操作完毕,释放资源!/n");
100: if( -1==V() )
101: {
102: printf("V操作失败!");
103: exit(1);
104: } ......
1: void P(int semid)
2: {
3: struct sembuf sem_p;
4: sem_p.sem_num = 0;
5: sem_p.sem_op = -1;
6: sem_p.sem_flg = SEM_UNDO;
7: if (semop(semid, &sem_p, 1) == -1) {
8: perror("p op failed/n");
9: exit(1);
10: }
11: }
12:
13: void V(int semid)
14: {
15: struct sembuf sem_p;
16: sem_p.sem_num = 0;
17: sem_p.sem_op = 1;
18: sem_p.sem_flg = SEM_UNDO;
19: if (semop(semid, &sem_p, 1) == -1) {
20: perror("v op failed/n");
21: exit(1);
22: }
23: }
PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。
信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前可用资源的数量。有两种实现方式:1)semaphore的取值必须大于或等于0。0表示当前已没有空闲资源,而正数表示当前空闲资源的数量;2) semaphore的取值可正可负,负数的绝对值表示正在等待进入临界区的进程个数。
信号量是由操作系统来维护的,用户进程只能通过初始化和两个标准原语(P、V原语)来访问。初始化可指定一个非负整数,即空闲资源总数。
P原语:P是荷兰语Proberen(测试)的首字母。为阻塞原语,负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;
V原语:V是荷兰语Verhogen(增加)的首字母。为唤醒原语,负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。
具体PV原语对信号量的操作可以分为三种情况:
1)把信号量视为一个加锁标志位,实现对一个共享变量的互斥访问。
实现过程:
P(mutex); // mutex的初始值为1 访问该共享数据;
V(mutex);
非临界区
2)把信号量视为是某种类型的共享资源的剩余个数,实现对一类共享资源的访问。
实现过程:
P(resource); // resource的初始值为该资源的个数N 使用该资源;
V(resource); 非临界区
3)把信号量作为进程间的同步工具
实现过程:
临界区C1;
P(S);
V(S);
临界区C2;