进程间通信之信号量

在学习进程间通信时,信号量可能是我们会感到有点头痛的地方。事实上,我们知道了信号量的作用再去理解信号量就会容易很多。先将信号量(System V信号量)介绍如下:
1)为什么需要使用信号量?
因为我们的系统资源是有限的,但是系统是多任务的,所以存在多进程、多线程,可能同时需要对某一个资源进行访问,用来保证资源的有序访问。
否则会产生不可预计的结果。
2)信号量是什么东西?
信号量是一个计数值,这个值表示当前可用的资源数,这个值也是可用是一个负数,负数的话,表示的是等待的进程数量。还有一个指针,这个指针指向等待该信号量的进程
P操作,减一操作。
V操作,加一操作。
操作属于原子操作,是不可被打断一种操作,一定要等待这个操作的完成。要包含的代码要尽量段,而且不要有循环。

3)如何使用信号量?
    (1)、产生一个key值。
    SYNOPSIS
            #include <sys/types.h>
            #include <sys/ipc.h>

            key_t ftok(const char *pathname, int proj_id);
        pathname  (which  must  refer  to an existing, accessible file) 这个路径必须是一个存在的、可访问的的文件
        proj_id (which must be nonzero) to generate a  key_t  必须是一个非零的数字,然后通过这个函数就会产生一个key_t类型的标示符。

        返回值:成功就会返回 对应的key值,错误返回-1,并且可以把对应的错误码打印出来看一下是什么错误。

    (2)、根据这个key值,去创建一个信号量。不同进程就可以根据这个信号量的ID去找到这个信号量,然后操作它。
        SYNOPSIS
                #include <sys/types.h>
                #include <sys/ipc.h>
                #include <sys/sem.h>

                    int semget(key_t key, int nsems, int semflg);
                    key: 就是我们第一步通过ftok产生的key值。
                    nsems:信号量的个数。
                    semflg:
                        创建  IPC_CREAT | 权限位  例如: IPC_CREAT | 0664 


    (3)、要对这个信号量,进行初始化。
        SYNOPSIS
                #include <sys/types.h>
                #include <sys/ipc.h>
                #include <sys/sem.h>
                int semctl(int semid, int semnum, int cmd, ...);
                semid:是创建的信号量的ID
                semnum:表示是对第几个信号量来进行设置。
                cmd:SETVAL 设置信号量的初始值。

                第四个参数,取决于第三个参数,如果有第四个参数的话,它是一个联合体,这个联合体是这样定义的
                           union semun {
                                int              val;    /* Value for SETVAL */
                                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
                                unsigned short  *array;  /* Array for GETALL, SETALL */
                                struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                            (Linux-specific) */
                        };

    (4)、调用这个信号量去保护临界资源。调用P操作,用完之后,调用V操作。

        SYNOPSIS
                #include <sys/types.h>
                #include <sys/ipc.h>
                #include <sys/sem.h>
                    int semop(int semid, struct sembuf *sops, unsigned nsops);
                    semid:这个信号量的ID
        struct sembuf, containing the following members:

                unsigned short sem_num;  /* semaphore number操作第几个信号量 */
                short          sem_op;   /* semaphore operation  P还是V操作,如果是互斥式的信号量,P操作,这个就等于-1,如果是v操作就等于1 */
                short          sem_flg;  /* operation flags  SEM_UNDO */

        成功返回0,失败返回-1;

具体代码如下:

#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include <stdio.h>

union semun
{
 int val;
 struct semid_df*buf;
 unsigned short *array;
};
int sem_p(int semid)
{
    struct sembuf sbuf;
    sbuf.sem_num=0; //表示对第几个信号量作操作
    sbuf.sem_op=-1;//p操作,为减一操作
    sbuf.sem_flg=SEM_UNDO; //默认使用这个标志位
    if((semop(semid,&sbuf,1))==-1) //去设置这个信号量
    {
        perror("p op fail");
        return -1;
    }
    return 0;
}
int sem_v(int semid)
{
    struct sembuf sbuf;
    sbuf.sem_num=0;
    sbuf.sem_op=1;
    sbuf.sem_flg=SEM_UNDO;
    if((semop(semid,&sbuf,1))==-1)
    {
        perror("p op fail");
        return -1;
    }
    return 0;
}
int main()
{
 key_t key;
 int semid,ret;
 pid_t pid;
 union semun tmp;
 key =ftok("/home/gec",2);//产生一个key值,用来创建一个信号量
 printf("key=%d",key);
 if(key<0)
 {
  perror("ftok fail");
  exit(1);
 }
 //创建这个信号量 1代表信号量的个数,返回信号量的ID
 semid = semget(key,1,IPC_CREAT|0777);

 if(semid<0)
 {
  perror("semget fail");
  exit(-1);
 }
 tmp.val=0;
 //初始化信号量的初始值为0,所以下面的操作就要先用v操作来释放这个信号量
 ret=semctl(semid,0,SETVAL,tmp);

 if(ret<0)
 {
  perror("semctl fail");
  exit(-1);
 }
 pid = fork();

 if(pid < 0)
 {
     perror("fork fail");
     exit(-1);
 }

 if(pid == 0)
 {
     //因为信号量的初始值为0,所以不用p操作,如果这里也用p操作大家都会锁死
//   sem_p(semid);
     sleep(3);
     printf("Child pid :%d\n",getpid());
     sem_v(semid);//完了之后用V操作,回覆信号量初始值为1
 }
 else if(pid > 0)
 {
     //父进程等待信号量的恢复,恢复了之后,执行P操作,然后往下执行
     sem_p(semid);
     printf("Parent pid:%d\n",getpid());
     //用完之后,执行V操作,恢复信号量
     sem_v(semid);
     //用完之后,把这个信号量删除 
     semctl(semid,0,IPC_RMID,tmp);
 }


 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值