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 手動刪除信號量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章