Linux下进程间通讯------信号量

在具体介绍信号量之前,先来了解一下在这一块有关的概念:

  • 临界资源:同一时刻,只允许一个进程访问的资源;
  • 临界区:访问临界资源的代码;
  • 原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的;
  • p操作:减一操作,从内存中获取资源,给进程使用;
  • v操作:加一操作,释放资源,将资源归还给内存;
  • 阻塞与非阻塞:**关注的是进程在发出请求后的状态。
  1. 阻塞:发出请求后,若请求不能被立即响应,则进程被挂起,等待请求响应。
  2. 非阻塞:发出请求后,无论请求能否响应,进程都会继续向下执行。
  • 同步、异步: **关注的是进程发出请求后,进程如何获知请求的条件已经发送
  1. 同步:发出请求后,如果进程不能立即响应,则不断去探测条件是否发生(类似心跳包机制)
  2. 异步:发出请求后,进程继续往下执行,但是在请求的条件发生后,会通过内核消息通知机制,通知进程条件已经发生。

接下来我们再来具体了解一下信号量:

信号量:类似于一个计数器,用于进程同步控制,而不是用于存储进程间通信数据。 

用到的重要函数:

#include <sys/sem.h>

int semget(key_t key, int num_sems, int sem_flags);// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1

int semop(int semid, struct sembuf semoparray[], size_t numops);// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1  
 
int semctl(int semid, int sem_num, int cmd, ...);// 控制信号量的相关信息
 

当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1;如果是引用一个现有的集合,则将num_sems指定为0。

在semop函数中,sembuf结构中定义如下: 

 struct sembuf{  
    short sem_num;   //除非使用一组信号量,否则它为0  
    short sem_op;   //信号量在一次操作中需要改变的数据,通常是两个数,                                         
                    //一个是-1,即P(等待)操作,  
                    //一个是+1,即V(发送信号)操作。  
    short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号量,  
                  //并在进程没有释放该信号量而终止时,操作系统释放信号量  
};  

模拟实现信号量,来进行进程控制:

#include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
     
// 联合体,用于semctl初始化
union semun
{
    int val; /*for SETVAL*/
    struct semid_ds *buf;
    unsigned short  *array;
};
   
// 初始化信号量
int init_sem(int sem_id, int value)
{
    union semun tmp;
    tmp.val = value;
    if(semctl(sem_id, 0, SETVAL, tmp) == -1)
    {
        perror("Init Semaphore Error");
        return -1;
    }
    return 0;
}
   
// P操作:
//    若信号量值为1,获取资源并将信号量值-1 
//    若信号量值为0,进程挂起等待
int sem_p(int sem_id)
{
    struct sembuf sbuf;
    sbuf.sem_num = 0; /*序号*/
    sbuf.sem_op = -1; /*P操作*/
    sbuf.sem_flg = SEM_UNDO;
  
    if(semop(sem_id, &sbuf, 1) == -1)
    {
        perror("P operation Error");
        return -1;
    }
    return 0;
}
 
// V操作:
//    释放资源并将信号量值+1
//    如果有进程正在挂起等待,则唤醒它们
int sem_v(int sem_id)
{
    struct sembuf sbuf;
    sbuf.sem_num = 0; /*序号*/
    sbuf.sem_op = 1;  /*V操作*/
    sbuf.sem_flg = SEM_UNDO;
 
    if(semop(sem_id, &sbuf, 1) == -1)
    {
        perror("V operation Error");
        return -1;
    }
    return 0;
}
 
// 删除信号量集
int del_sem(int sem_id)
{
    union semun tmp;
    if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
    {
        perror("Delete Semaphore Error");
        return -1;
    }
    return 0;
}
 
int main()
{
    int sem_id;  // 信号量集ID
    key_t key;  
    pid_t pid;
    
    // 获取key值
    if((key = ftok(".", 'z')) < 0)
    {
        perror("ftok error");
        exit(1);
    }
    
    // 创建信号量集,其中只有一个信号量
    if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
    {
        perror("semget error");
        exit(1);
    }
  
    // 初始化:初值设为0资源被占用
    init_sem(sem_id, 0);

    if((pid = fork()) == -1)
    perror("Fork Error");
    else if(pid == 0) /*子进程*/ 
    {
        sleep(2);
        printf("Process child: pid=%d\n", getpid());
        sem_v(sem_id);  /*释放资源*/
    }
    else  /*父进程*/
    {
        sem_p(sem_id);   /*等待资源*/
        printf("Process father: pid=%d\n", getpid());
        sem_v(sem_id);   /*释放资源*/
        del_sem(sem_id); /*删除信号量集*/
    }
    return 0;
}

执行结果:

上面的例子如果不加信号量,则父进程会先执行完毕。这里加了信号量让父进程等待子进程执行完以后再执行。

附:

进程间通讯--------管道

https://blog.csdn.net/YANG_1605/article/details/96724987

进程间通讯--------共享内存

https://blog.csdn.net/YANG_1605/article/details/99321789

进程间通讯--------消息队列

https://blog.csdn.net/YANG_1605/article/details/99290536

进程间通讯--------socket

https://blog.csdn.net/YANG_1605/article/details/99440305

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值