Linux进程通信之信号量

进程通信之信号量

信号量机制是为了处理操作系统在对于临界资源的管理过程中,容易出现多个程序同时访问一个共享资源,然后引发死锁,误读等问题所提出的一种解决方案。

信号量是一个特殊的变量,只能对其进行等待和发送两种操作。等待操作是P操作,当信号量值为0的时候,程序等待,当值大于0的时候,使值减一,程序继续执行。发送操作是V操作,如果有进程等待,则唤起该进程,否则的话,将信号量的值加1.


#include <sys/sem.h>

//创建/获取信号量
int semget(key_t key, int num_sems, int sem_flags);
/*
参数:
key是整数值(唯一非零),就是Linux线程操作中经常用到的键值,可以通过ftok函数得到,不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

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

sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

返回值:semget函数成功返回一个相应信号标识符(非零),失败返回-1
*/
//修改信号量p v操作
int semop(int semid, struct sembuf *sops, size_t numops);
/*
参数:
semid:信号量集的标识符
sembuf:结构体格式如下 结构体指针(参数注意&)
    struct sembuf{  
        short sem_num;//信号在信号集中的索引,0代表第一个信号,1代表第二个信号 
        short sem_op;//操作类型: 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,  
                //一个是+1,即V(发送信号)操作。 
                 结构体中的sem_op 参数:
                    sem_op > 0
                    信号加上 sem_op 的值,表示进程释放控制的资源;
                    sem_op = 0
                    如果sem_flg没有设置IPC_NOWAIT,则调用进程进入睡眠状态,直到信号量的值为0;否则进程不会睡眠,直接返回 EAGAIN
                    sem_op < 0
                    信号加上 sem_op 的值。若没有设置 IPC_NOWAIT ,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN 
        short sem_flg;//操作标志: 通常为SEM_UNDO,使操作系统跟踪信号,  
                    //并在进程没有释放该信号量而终止时,操作系统释放信号量
                    该参数可设置为 IPC_NOWAIT 或 SEM_UNDO 两种状态,通常为SEM_UNDO  
    };  
numops:指出将要进行操作的信号的个数
*/

//初始化和删除
int semctl(int sem_id,int sem_num,int command,[union semun sem_union]);
/*
semid :信号量集的标识符;
sem_num:即将要进行操作的信号量的编号,即信号量集合的索引值,其中第一个信号量的索引值为0。
    semun:是一个联合体,一般用到val,表示要传给信号量的初始值
    union semun{  
      int val;  //一般用到val,表示要传给信号量的初始值
      struct semid_ds *buf;  
      unsigned short *array;  
      struct seminfo *__buf;
    }; 

command:将要在集合上执行的命令,通常用特定的宏代替:
    常用的宏有;
    SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
        eg:初始化时, semctl(semid,0,SETVAL,initsem);
    IPC_RMID:从内核删除该信号量集合
        eg:删除信号量 semctl(semid,0,IPC_RMID)
      
*/
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

//联合类型semun定义 
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) */
};

// V操作:
//  释放资源并将信号量值+1
//  如果有进程正在挂起等待,则唤醒它们
void vbackkey(int id)
{
        struct sembuf set;
        set.sem_num = 0;  //信号量编号  对第一个信号量操作
        set.sem_op = 1;  //V操作,加一
        set.sem_flg = SEM_UNDO;
        semop(id,&set,1); //参数2为结构体指针 &set
        printf("backkey ok\n");
}

// P操作:
//  若信号量值为1,获取资源并将信号量值-1 
//  若信号量值为0,进程挂起等待
void pgetkey(int id)
{
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = -1; //P操作,减一
        set.sem_flg = SEM_UNDO;
        semop(id,&set,1);
        printf("getkey ok\n");
}
int main(int argc,char **argv)
{
        key_t key;
        key = ftok(".",2);
        int semid;
  
    // 创建一个新的信号量或者是取得一个已有信号量的键
        semid = semget(key,1,IPC_CREAT|0666);
    
    //初始化
        union semun initsem;
        initsem.val = 0;  //将信号量值设为0!目的为了父子进程先后顺序
        semctl(semid,0,SETVAL,initsem);//semtcl进行初始化


        int pid = fork();
        if(pid > 0)//父进程
        {
        //因为将信号量初始化为0,所以父进程进行P操作,父进程会被挂起等待
        //待子进程进行V操作后,方可继续执行
        pgetkey(semid);
                printf("this is father\n");
                vbackkey(semid);
        
        //删除信号量
                semctl(semid,0,IPC_RMID);
        }
        else if(pid == 0)//子进程
        {
                //父进程被挂起,子进程就会先执行printf
                printf("this is child\n");
                vbackkey(semid);
                //子进程进行V操作,val加1,父进程就可以继续执行p
        }
        else
        {
                printf("fork error\n");
        }
}


线程同步

linux 信号量相关函数都声明头文件 semaphore.h 头文件中,所以使用信号量之前需要先包含头文件

#include <semaphore.h>

信号量的创建就像声明一般的变量一样简单,例如:sem_t sem,之后对该信号量进行初始化和使用。

sem_init
该函数用于创建信号量,其原型如下:

int sem_init(sem_t *sem, int pshared, unsigned int value);

该函数初始化由 sem 指向的信号对象,并给它一个初始的整数值 value。

pshared 控制信号量的类型,值为 0 代表该信号量用于多线程间的同步,值如果大于 0 表示可以共享,用于多个相关进程间的同步

参数 pshared > 0 时指定了 sem 处于共享内存区域,所以可以在进程间共享该变量

sem_wait

int sem_wait(sem_t *sem); 
int sem_trywait(sem_t *sem);
 

sem_wait 是一个阻塞的函数,测试所指定信号量的值,它的操作是原子的。若 sem value > 0,则该信号量值减去 1 并立即返回。若sem value = 0,则阻塞直到 sem value > 0,此时立即减去 1,然后返回。

sem_trywait 函数是非阻塞的函数,它会尝试获取获取 sem value 值,如果 sem value = 0,不是阻塞住,而是直接返回一个错误 EAGAIN。

sem_post
把指定的信号量 sem 的值加 1,唤醒正在等待该信号量的任意线程。

int sem_post(sem_t *sem);

sem_getvalue

int sem_getvalue(sem_t *sem, int *sval);

获取信号量 sem 的当前值,把该值保存在 sval,若有 1 个或者多个线程正在调用 sem_wait 阻塞在该信号量上,该函数返回阻塞在该信号量上进程或线程个数。

sem_destroy
该函数用于对用完的信号量的清理。它的原型如下:

int sem_destroy(sem_t *sem);

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

Linux线程——线程创建和基本使用(多线程并发)_linux 线程-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会掉头发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值