linux进阶39——信号量

1. 概念

信号量与其他进程间的通信方式不大相同,主要用途是保护临界资源。进程可以根据它判断是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。

2. 分类

二值信号量:信号量的值只能取0或1,类似于互斥锁。但两者有不同:信号量强调共享资源,只要共享资源可用,其他进程同样可以修改信号量的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。

计数信号量:信号量的值可以取任意非负值。

3. 创建信号量

3.1 函数定义

#include 
#include 
#include  

int semget(key_t key,int nsems,int flags)

3.2 返回值

成功返回信号集ID,失败返回-1

3.3 参数

  • key: 键值,由ftok获得;
  • nsems: 指定打开或者新创建的信号量集中将包含信号量的数目;
  • semflg: 标识,同消息队列;

4. 改变信号量

4.1 函数定义

int semop(int semid,struct sembuf *sops,size_t nops);

对信号量进行控制。

4.2 返回值

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

4.3 参数

  • semid: 信号量集的ID;
  • sops: 是一个操作数组,表明要进行什么操作;

sembuf的定义如下:

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

5. 删除和初始化信号量

5.1 函数定义

int semctl(int semid,int semnum,int cmd, ...);

5.2 返回值

linux进阶39——信号量_第1张图片

 5.3 参数

如有需要第四个参数一般设置为union semnu arg;定义如下:

union semun
{ 
    int val; //使用的值
    struct semid_ds *buf; //IPC_STAT、IPC_SET 使用的缓存区
    unsignedshort *arry; //GETALL,、SETALL 使用的数组
    struct seminfo *__buf;// IPC_INFO(Linux特有) 使用的缓存区
};
  • semid: semget返回的信号量标识符;
  • semnum: 当前信号量集的哪一个信号量
  • cmd: 通常为下面两个值
    SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
    
    IPC_RMID:用于删除一个已经无需继续使用的信号量标识符,删除的话就不需要缺省参数,只需要三个参数即可。

6. 示例

#include 
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include 
using namespace std;
 
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};
 
static int sem_id = 0;  
  
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"); 
    else
        fprintf(stdout, "已经删除信号量\n"); 
 
}  
  
static int semaphore_p()  
{  
    //对信号量做减1操作,即等待P(sv)  
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = -1;//P()  
    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()  
{  
    //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)  
    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;  
}  
  
int main(int argc, char *argv[])  
{  
    char message = 'S';  
    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);  
    }  
    cout< 1)  
    {  
        //如果程序是第一次被调用,则在退出前删除信号量  
        sleep(3);  
        del_semvalue();  
    }  
    exit(EXIT_SUCCESS);  
}  

编译运行:

[root@192 semaphore]# g++ semaphore.cpp -o semaphore
[root@192 semaphore]# ./semaphore 1
21
进入1离开1
进入1离开1
进入1离开1
进入1离开1
进入1离开1
进入1离开1
进入1离开1
进入1离开1
进入1离开1
进入1离开1

1913 - finished
已经删除信号量
[root@192 semaphore]# 

部分参考:https://blog.csdn.net/qq_30168505/article/details/53041825#

你可能感兴趣的:(linux进阶,信号量)