進程間通信之信號量

             在學習了進程間通信的前幾種方式管道,消息隊列、共享內存之後,進程間通信的另一種方式——信號量。

            在學習信號量之前,需要先理解幾個名詞。

            互斥:由於各進程要求共享資源,而有些資源需要互斥使用,因此各進程間需要競爭使用這些資源,這種關係爲進程的互斥。

            同步:多個進程需要相互配合共同完成一項任務。

            臨界資源:系統中某些資源一次只允許一個進程使用,稱這樣的資源爲臨界資源或互斥資源。

            臨界區:在進程中涉及到互斥資源的程序段叫臨界區。

1、信號量

            信號量本質上就是一個計數器,用來衡量臨界資源的個數。但與此同時,信號量也是一個臨界資源。

            信號量的結構體僞代碼:

struct  semaphore
{
    int value;
    pointer_PCB queue;
}

            信號量值得含義:

               S>0:  S表示可用資源的個數;

              S=0:  表示無可用資源,無進程等待;

              S<0:  |S|表示等待隊列中等待進程的個數;

2、 P、V原語          

         信號量中最重要的就是P、V操作。

        P操作:減1操作     就是向操作系統申請資源,申請成功,進行減1操作;

        V操作:加1操作     就是向操作系統歸還資源,歸還成功,進行加1操作;

        P、V操作均爲原子操作,在使用時,需要保證P、V操作的原子性。這也從側面說明了爲什麼不定義一個全局變量int count,而需要信號量集的出現。

3、 信號量集結構

    

  4、信號量集函數

            (1)創建信號量集的函數

              int semget(key_t key, int nsems, int semflg);

             參數:key:(同消息隊列和共享內存)信號量集的名字;

                      nems:信號量集中信號量的個數;

                      semflg:(同消息隊列和共享內存)和創建文件時使用的mode標誌一樣的。 

IPC_CREAT|IPC_EXCL:不存在創建,存在出錯返回;IPC_CREAT:不存在創建,存在返回;

             返回值創建成功返回信號量集的標識符,創建失敗返回-1

            (2)控制信號量集的函數

             int semctl(int semid, int semnum, int cmd, ...);

             參數:semid:semget返回的信號量集的標識符;

                       semnum:要對信號量集中的第幾個信號量進行操作;

                       cmd:將要採取的動作,不同的可取值有不同的含義。如下圖

   

          在使用信號量集之前必須對信號量集進行初始化,相當於要把cmd設置爲SETVAL,這時候就要在加一個參數,這個參數是一個聯合體。


              不過,這個聯合體中我們只使用第一個變量val。

              返回值:成功返回0,失敗返回-1

            (3)訪問信號量集的函數

            int semop(int semid, struct sembuf *sops, unsigned nsops);

            參數:semid:senget返回的信號量集的標識符;

                      sops:是一個指向結構數組的指針;

                      nsops:信號量的個數;

              說明一下,這個結構體struct sembuf


  5、信號量的實現

comm.h

#ifndef _COMM_H_
#define _COMM_H_

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<unistd.h>
#define PATHNAME "."
#define PROJ_ID 0X6666



int creatsem(int nums);
int initsem(int semid,int nums,int initval);
int getsem(int nums);
int p(int semid,int who);
int v(int semid,int who);
int destroysem(int semid);

#endif

test_sem.c

#include "comm.h"
union semun
{
	int val;
};
int commsem(int nums, int flags)
{
	key_t key = ftok(PATHNAME, PROJ_ID);
	if (key<0)
	{
		perror("ftok");
		return -1;
	}
	int semid = semget(key, nums, flags);
	if (semid<0)
	{
		perror("semget");
		return -2;
	}
	return semid;
}

int creatsem(int nums)
{
	return commsem(nums, IPC_CREAT | IPC_EXCL | 0666);
}

int initsem(int semid, int nums, int value)
{
	union semun u;
	u.val = value;
	if (semctl(semid, nums, SETVAL, u)<0)
	{
		perror("semctl");
		return -1;
	}
	return 0;
}

int getsem(int nums)
{
	return commsem(nums, IPC_CREAT);
}

int commpv(int semid, int who, int op)
{
	struct sembuf sf;
	sf.sem_num = who;
	sf.sem_op = op;
	sf.sem_flg = 0;
	if (semop(semid, &sf, 1)<0)
	{
		perror("semop");
		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 destroyaem(int semid)
{
	if (semctl(semid, 0, IPC_RMID)<0)
	{
		perror("semctl");
		return -1;
	}
}
int main()
{

	int semid = creatsem(1);
	initsem(semid, 0, 1);
	pid_t id = fork();
	int _semid = getsem(0);
	if (id == 0)
 	{// child
		while (1)
		{
		//	p(_semid, 0);
			printf("A");
			fflush(stdout);
			usleep(100000);
			printf("A ");
			fflush(stdout);
			usleep(300000);
		//	v(_semid, 0);
		}

	}
	else
	{//father
		while (1)
		{
		//	p(_semid, 0);
			printf("B");
			fflush(stdout);
			usleep(200000);
			printf("B ");
			fflush(stdout);
			usleep(100000);
		//	v(_semid, 0);
		}
		wait(NULL);
	}
	destroysem(semid);
	return 0;

}

    測試結果:

       通過結果截圖我們可以看出,在不加P、V操作之前,輸出的並不是我們想要的兩個相同的字符成對出現的情況。


        此時,顯示器只有一個,兩個進程同時打印,此時顯示器成爲臨界資源,這就需要我們加互斥鎖來進行保護。互斥鎖就是二元信號量,

       當使用互斥鎖進行保護後(即加上P、V操作之後),屏幕上就會成對成對的打印我們所想要的字符,而不會出現交叉打印的現象。

 

       和其他進程間通信的方式一樣,信號量依然可以使用ipcs -s命令來查看,使用ipcrm -s+標號進行刪除。


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