进程通信-信号量semaphore

semaphore:信号量 头文件:include <sys/sem.h>
int semget(key_t key,int num_sems,int sem_flags);创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
功能:创建一个新信号量或取得一个已有信号量的键。
参数:key:不相关的进程可以通过该整数值访问同一个信号量。
         num_sems:指定需要的信号量的数目。
         sem_flags:一组标志。
返回:成功时返回一个正数;失败返回-1.
    
int semop(int semid, struct sembuf semoparray[],size_t numops);对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1 
int semop(int sm_id, struct sembuf *sem_ops, size_t num_sem_ops);  
功能:用于改变信号量的值。
参数:sm_id:由semget返回的信号量标识符。
         sem_ops:指向要操作的结构数组的指针。
         num_sem_ops:要操作的结构数组的元素个数。
返回:成功返回0;失败返回-1.
    
int semctl(int semid, int sem_num, int cmd,  union semun arg);控制信号量的相关信息
功能:用来直接控制信号量信息。
参数:sem_id:由semget返回的信号量标识符。
         sem_num:信号量编号。
         comd:要采取的操作,一些命令可以查资料。
返回:成功返回0;失败返回-1.
union semun {
   short val;          //SETVAL用的值
   struct semid_ds* buf; //IPC_STAT、IPC_SET用的semid_ds结构
   unsigned short* array; //SETALL、GETALL用的数组值
   struct seminfo *buf;   //为控制IPC_INFO提供的缓存
};


当semget创建新的信号量集合,必须指定集合中信号量的个数(num_sems),通常为1,如果引用一个现有的集合,则将num_sems指定为0 
struct sembuf 
{
    short sem_num; // 信号量组中对应的序号,0~sem_nums-1
    short sem_op;  // 信号量值在一次操作中的改变量
    short sem_flg; // IPC_NOWAIT, SEM_UNDO
}


信号量semaphore,它是一个计数器,信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
1.特点
信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
支持信号量组。
2.原型
最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。
而可以取多个正整数的信号量被称为通用信号量。
在semctl函数中的命令有多种,这里就说两个常用的:
SETVAL:用于初始化信号量为一个已知的值,所需要的值作为联合semun的val成员来传递,
在信号量第一次使用之前需要设置信号量。
IPC_RMID:删除一个信号量集合,如果不删除信号量,它将继续在系统中存在,即使程序已经退出,

它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

#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;  //ID号
    pid_t pid;
/************************************************************************
系统建立IPC通讯(消息队列.信号量和共享内存)时必须指定一个ID值,该id值通过ftok函数得到
原型:key_t ftok(const char * fname, int id)
参数:fname是文件路径一般取当前目录,id是子ID号
返回值:一般将文件的索引节点取出加上id号为返回值 文件的索引节点ls -a查看
eg:如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制
为0x26,则最后的key_t返回值为0x26010002。
*************************************************************************/
    if((key = ftok(".", 'z')) < 0)// 获取key值
    {
        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;
}


猜你喜欢

转载自blog.csdn.net/linuxzhuxiaodi/article/details/78533685
今日推荐