Linux——进程间通信——信号量

信号量是进程间通信方式的其中一种,那么为什么存在信号量这种机制呢?

信号量主要应用于同步互斥操作,先来了解一下,同步和互斥是什么?
同步:多个进程需要相互配合才能完成一项任务
互斥:1.由于各个进程都要访问共享资源,而且这些资源需要排它使用,因此各个进程间需要竞争使用这些资源,我们将这种关系称为进程的互斥。
2.系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或者互斥资源
3.在进程中涉及到互斥资源的程序的叫临界区

互斥:P和V操作在同一个进程
同步:P和V操作不在同一个进程中
信号量值含义:
S>0: S表示可用资源个数
S=0:S表示无可用资源,无等待进程
S<0:|S|表示等待队列中进程个数


p原语:
p(s)
{
s.value --;
if(s.value < 0){
        将该进程置为等待状态
        将该进程的task_struct插入到相应的等待队列
}
};

V原语:
V(s)
{
s.value ++;
if(s.value <= 0)
{
        唤醒等待队列上的进程
        将其改成就绪状态
        放入就绪队列
}
}

我们了解了信号量的作用和原理,下面我们看看信号量集是怎么样的:
信号量集创建:

创建:
int semget(key_t key, 
            int nsems, //信号量的个数    0
            int semflg);//IPC_CREAT|0644  0

设置初值:
union semun{
int val;    /*value for SETVAL*/
};

int semctl(int semid, 
            int semnum,//第几个信号量 0,...........
             int cmd, //SETVAL
...);//semun 的值


获得信号量当前值:
int semctl(int semid, 
                int semnum,//获得第几个信号量的当前值
                 int cmd, //GETVAL
);
返回值:是信号量的当前值

设置PV操作:
int semop(int semid, 
                struct sembuf *sops,//数组
                 unsigned nsops);//数组大小

struct sembuf{
        unsigned short sem_num; //要操作第几个信号量
        short       sem_op;  /* -1(p)  1(v)*/
        short       sem_flg;   //  0
}

下面是实验代码:
两个进程对标准输出互斥打印:
comm.h

#ifndef _COMM_H
#define _COMM_H

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


union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *_buf;
};

int createSemSet(int nums);
int initSem(int semid,int nums,int initVal);
int getSemSet(int nums);
int P(int semid,int who);
int V(int semid,int who);
int destroySemSet(int semid);

#endif

comm.c

#include "comm.h"

static int commSemSet(int nums,int flags)
{
    key_t key = ftok(".",0x6666);
    if(key < 0)
    {
        perror("ftok\n");
        return -1;
    }

    int semid = semget(key,nums,flags);
    if(semid < 0)
    {
        perror("semget\n");
        return -2;
    }

    return semid;
}

int createSemSet(int nums)                       //创建信号集
{
    return commSemSet(nums,IPC_CREAT|IPC_EXCL|06666);
}

int getSemSet(int nums)
{
    return commSemSet(nums,IPC_CREAT);
}

int initSem(int semid,int nums,int initVal)     //初始化信号集
{
    union semun _un;
    _un.val = initVal;

    if(semctl(semid,nums,SETVAL,_un) < 0)
    {
        perror("semctl\n");
        return -1;
    }

    return 0;
}

static int commPV(int semid,int who,int op)            //PV操作
{
    struct sembuf _sf;
    _sf.sem_num = who;
    _sf.sem_op = op;
    _sf.sem_flg = 0;

    if(semop(semid,&_sf,1) < 0)
    {
        perror("semop\n");
        return -1;
    }
    return 0;
}


int P(int semid,int who)
{
    return commPV(semid,who,-1);
}

int V(int semid,int who)
{
    return commPV(semid,who,1);
}

int destroySemSet(int semid)
{
    if(semctl(semid,0,IPC_RMID)<0)          //销毁信号集
    {
        perror("semctl\n");
        return -1;
    }
}

sem_test.c

#include"comm.h"
#include<wait.h>
int main ()
{
    int semid = createSemSet(1);
    initSem(semid,0,1);

    pid_t pid = fork();

    if(pid < 0)
    {
        perror("fork()\n");
        exit(1);
    }

    if(pid == 0)
    {
        int _semid = getSemSet(0);
        while(1)
        {
            P(_semid,0);
            printf("A");
            fflush(stdout);
            usleep(123456);
            printf("A");
            fflush(stdout);
            usleep(344323);
            V(_semid,0);
        }

    }else{
        while(1)
        {
            P(semid,0);
            printf("D");
            fflush(stdout);
            usleep(122222);
            printf("D");
            fflush(stdout);
            usleep(222222);
            V(semid,0);
        }
        wait(NULL);
    }

    destroySemSet(semid);
    return 0;

}

程序效果图:
A 和 D都是成对打印的,互斥访问stdout:
这里写图片描述

ipcs -s
这里写图片描述

可以使用 ipcrm -s semid 手动删除信号量

猜你喜欢

转载自blog.csdn.net/shawei_/article/details/81302876