linux信號量

1.什麼是信號量?
爲了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,它可以通過生成並使用令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨佔式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來調協進程對共享資源的訪問的。

2.信號量的工作原理
由於信號量只能進行兩種操作等待和發送信號,即P(sv)和V(sv),他們的行爲是這樣的:
P(sv):如果sv的值大於零,就給它減1;如果它的值爲零,就掛起該進程的執行
V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛起,就給它加1.


3.信號量操作函數
需要的庫
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

1.semget函數
用於創建一個新的或者獲取一個已經存在的信號量組
int semget(key_t key,int nsems, int semflg);

key 是信號量組的系統唯一的標記符,用戶可以自己定義。
nsems 指明信號量組中包含多小個信號量
flags 是創建時的一些選項

一、key對應的信號量組不存在時,則創建擁有nsems個信號量,semflg中的後9位如0666的666代表訪問權限。
二、key對應的信號量組存在時,返回信號量組的ID,參數nsems和semflg無意義。
三、特殊情況,semflg爲IPC_EXECL時,只要key對應的信號量組存在,則segmet返回-1。作用:同時使用IPC_EXECL和IPC_CREATE,獲取新的信號量組,如果已經存在,則返回-1;

2.semop函數
用於對多個信號量進行操作
int semop(int  semid, struct sembuf* sops, int nsops);

semid 對信號量組的ID。
sops 說明如何操作。
nsops 說明操作的個數。

對sembuf結構說明
struct sembuf{
short sem_num;
short sem_op;
short sem_flg;
}
sem_num 對信號量組的第sem_num個信號量進行操作
sem_op 在信號量組的第sem_num個信號量執行操作
sem _flg操作類型

sem_num是相對應的信號量集中的某一個資源,所以其值是一個從0到相應的信號量集的資源總數(ipc_perm.sem_nsems)之間的整數。除非使用一組訊號了,否則它的取值一般爲0。
sem_op指明需要進行的操作,如果他取+1,相當於P操作。取-1,相當於V操作。sem_op的值實際是對資源的佔有和釋放。sem_op<0,佔有可用資源;sem_op>0,釋放可用資源。對於每個信號量都保存sem_val,初始爲0.
sem_flg通常取0,如果取值爲SEM_UNDO,則進程退出時,自動平衡P和V操作。

3.semctl
用於對信號量組中信號量進行控制,如刪除,修改。
int semctl(int sem_id, int semnum, int cmd, union semun);

semid信號量組的ID。
sem_num是信號量的編號
cmd控制命令
union semun{
int val;
struct semid_ds *buf;
unsigned short *arry;
}

int val由SETVAL使用, semctl將會把arg.val設置到信號量的semval中.

struct semid_ds *buf由IPC_STAT和IPC_SET使用, 讀取時將信號量集合的semid_ds讀取到arg.buf中. 設置時使用arg.buf的三項內容.

unsigned short *array由GETALL和SETALL使用, 將讀取和設置集合中的所有信號量


參數cmd的命令:
IPC_STAT讀取一個信號量集的數據結構semid_ds,並將其存儲在semun中的buf參數中。
IPC_SET設置信號量集的數據結構semid_ds中的元素ipc_perm,其值取自semun中的buf參數。
IPC_RMID將信號量集從內存中刪除。
GETALL用於讀取信號量集中的所有信號量的值。
GETNCNT返回正在等待資源的進程數目。
GETPID返回最後一個執行semop操作的進程的PID。
GETVAL返回信號量集中的一個單個的信號量的值。
GETZCNT返回這在等待完全空閒的資源的進程數目。
SETALL設置信號量集中的所有的信號量的值。
SETVAL設置信號量集中的一個單獨的信號量的值。
返回值:如果成功,則爲一個正數


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <fcntl.h>

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};

static int sem_id = 0;

static int init_sem();
static void del_sem();
static int sem_P();
static int sem_V();

int main(int argc, char** argv)
{
    char show = 'X';
    int i = 0;
    sem_id = semget((key_t)1234,1,0666 | IPC_CREAT);
    if(argc > 1)
    {
        if(!init_sem())
        {
            printf("init failed\n");
            return -1;
        }
        show = argv[1][0];
        sleep(2);
    }
    for (i = 0; i< 10; i++)
    {
        if (!sem_P())
            exit(-1);
        printf("%c", show);
        fflush(stdout); sleep(rand()%3);

        printf("%c", show);
        fflush(stdout);
        if (!sem_V())
            exit(-1);
        if(argc >1)
        {
            sleep(3);
        }
        else
        {
            sleep(2);
        }
    }
    printf("\n%d finished \n",getpid());
    if (argc > 1)
    {
        sleep(10);
        del_sem();
    }
    exit(0);
}

static int init_sem()
{
    union semun union_sem;

    union_sem.val = 1;
    if (semctl(sem_id, 0, SETVAL, union_sem) == -1)
        return 0;
    return 1;
}
static void del_sem()
{
    union semun union_sem;

    if (semctl(sem_id, 0, IPC_RMID, union_sem) == -1)
        printf("delete failed\n ");
}

static int sem_P()
{

    struct sembuf sem;
    sem.sem_num = 0;
    sem.sem_op = -1;
    sem.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem, 1) == -1)
    {
        printf("sem_P failed\n");
        return 0;
    }
    return 1;
}

static int sem_V()
{
    struct sembuf sem;
    sem.sem_num = 0;
    sem.sem_op = 1;
    sem.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem, 1) == -1)
    {
        printf("sem_V failed\n");
        return 0;
    }
    return 1;
}
gcc sem.c -o sem
sem O & sem

此程序通過sleep控制O程序和X程序進入臨界區的頻率。






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