Chinaunix首页 | 论坛 | 博客
  • 博客访问: 752913
  • 博文数量: 130
  • 博客积分: 2951
  • 博客等级: 少校
  • 技术积分: 1875
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-04 18:32
文章分类

全部博文(130)

文章存档

2013年(1)

2012年(129)

分类: C/C++

2012-11-02 11:59:50

server.c
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <sys/types.h>
  4. #include <sys/sem.h>
  5. #include <time.h>

  6. #define TIME_SEM_KEY 500
  7. union semun { int val ; struct semid_ds *buf ; ushort *array; };
  8. int semset_id; /* global for cleanup() */

  9. main()
  10. {
  11.         union semun initval;
  12.         struct sembuf actions; /* action set */
  13.         int i ;
  14.         semset_id = semget( TIME_SEM_KEY, 1, (0666|IPC_CREAT|IPC_EXCL) ); 
  15.                                   //1表示创建一个信号量
  16.                                   //IPC_CREAT   如果共享内存不存在,则创建一个共享内存,否则打开操作。
                                      //IPC_EXCL只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
  17.         initval.val = 1;
  18.         semctl(semset_id, 0, SETVAL, initval); //0表示信号量集合里面的特定信号量的号码,从0开始
  19.                                                //SETVAL 表示给信号量设置初始值,即initval.val

  20.         printf("server begin\n");
  21.         printf("server sem num = %d\n",semctl(semset_id, 0, GETVAL, 0));
  22.         actions.sem_num = 0;
  23.         actions.sem_flg = SEM_UNDO; //使用SEM_UNDO的作用是,-1后加锁,如果程序意外退出,系统则会恢复此信号量                                    //,否则其他进程将一直阻塞在这个被锁的信号量这里。
  24.         actions.sem_op = -1 ;
  25.         semop( semset_id, &actions, 1); //-1操作后,信号量变为0
  26.         printf("server sem num = %d\n",semctl(semset_id, 0, GETVAL, 0));
  27.         for(i=0; i < 5; i++)
            {
                printf("server sleep %d\n", i);
  28.             printf("server sem num = %d\n",semctl(semset_id, 0, GETVAL, 0));
                sleep(i);                            //这时候启动client进程,client进程获得不到信号量,被阻塞
            }

            actions.sem_num = 0;       
            actions.sem_flg = SEM_UNDO;   
            actions.sem_op  = +1 ;   
            semop( semset_id, &actions, 1);

  29.         sleep(2);                                //这时候client进程才获得了信号量
            printf("server sem num = %d\n",semctl(semset_id, 0, GETVAL, 0));   
            semctl(semset_id, 0, IPC_RMID, initval);

    }

client.c

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <sys/types.h>
  4. #include <sys/sem.h>
  5. #include <time.h>

  6. #define TIME_SEM_KEY 500
  7. union semun { int val ; struct semid_ds *buf ; ushort *array; };
  8. int    seg_id, semset_id;            /* global for cleanup()    */

  9. main()
  10. {
  11.     pthread_t t1, t2;
  12.     union semun initval;
  13.     struct sembuf actions;    /* action set        */

  14.     semset_id = semget( TIME_SEM_KEY, 1, 0 );

  15.     actions.sem_num = 0;        /* sem[0] is n_readers    */
  16.     actions.sem_flg = SEM_UNDO;    /* auto cleanup        */
  17.     actions.sem_op = -1 ;    /* wait til no readers    */
  18.     printf("client begin\n");
  19.     semop( semset_id, &actions, 1); //此时信号量已经为0,所以-1操作会被阻塞
  20.     actions.sem_num = 0;        /* sem[0] is n_readers    */
  21.     actions.sem_flg = SEM_UNDO;    /* auto cleanup        */
  22.     actions.sem_op = +1 ;    /* wait til no readers    */
  23.     semop( semset_id, &actions, 1);
  24.     printf("client over\n");
  25. }

运行结果:
./server
server begin
server sem num = 1
server sem num = 0
server sleep 0
server sem num = 0
server sleep 1
server sem num = 0
server sleep 2
server sem num = 0
server sleep 3
server sem num = 0
server sleep 4
server sem num = 0
server sem num = 1

./client
client begin
client over

comments:
server进程运行后,输出此时信号量是1, -1操作后锁住信号量,输出信号量是0,然后进入睡眠
client进程运行后,输出"client begin“后,-1操作被阻塞在那里,因为此时信号量已经为0,等待server醒来
server醒来后,+1操作释放信号量,将信号量值变为1, 进入睡眠
client进程获得可用信号量,-1操作锁住信号量,+1操作释放信号量,运行结束,输出client over
server醒来后,输出此时信号量是1,并结束

一些信号量的理论知识:
P1和P2是分别将数据送入缓冲B和从缓冲B读出数据的两个进程, 为了防止这两个进程并发时产生错误,狄克斯特拉设计了一种同步机制叫“PV操作”,P操作和V操作是执行时不被打断的两个操作系统原 语。
1. 执行P操作P(S)信号量S值减1,若结果不为负则P(S)执行完毕,否则执行P操作的进程暂停等待释放。
2. 执行V操作V(S)时,S的值加1, 若结果不大于0则释放一个因执行P(S)而等待的进程。

对P1和P2可定义两个信号量S1和S2,初值分别为1和0。
1. 进程P1在向缓冲B送入数据前执行P 操作P(S1),在送入数据后执行V操作V(S2)。
2. 进程P2在从缓冲B读取数据前先执行P操作P(S2),在读出数据后执行V操作V(S1)。
3. 当P1往 缓冲B送入一数据后信号量S1之值变为0,在该数据读出后S1之值才又变为1,因此在前一数未读出前后一数不会送入,从而保证了P1和P2之间的同步。

一般来说,信号量S>=0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有 可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S<0,表示有某些 进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。

有个问题是server的进程在循环的时候,这时候client进程已经运行并由于尝试对信号量做-1操作而导致信号量<0,从而client被阻塞,那么这时候为啥从server里获得的当前的信号量的值还是0呢?为啥不是-1呢?
阅读(3890) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~