进程间通信之信号量

             在学习了进程间通信的前几种方式管道,消息队列、共享内存之后,进程间通信的另一种方式——信号量。

            在学习信号量之前,需要先理解几个名词。

            互斥:由于各进程要求共享资源,而有些资源需要互斥使用,因此各进程间需要竞争使用这些资源,这种关系为进程的互斥。

            同步:多个进程需要相互配合共同完成一项任务。

            临界资源:系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。

            临界区:在进程中涉及到互斥资源的程序段叫临界区。

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+标号进行删除。


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值