`
tomotoboy
  • 浏览: 166830 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

进程间通信——信号量

阅读更多
有关结构体
1.sem
struct sem {
                 short   sempid;        /* pid of last operation */
                 ushort  semval;        /* current value */
                 ushort  semncnt;       /* num procs awaiting increase in semval */
                 ushort  semzcnt;       /* num procs awaiting semval = 0 */
};
其中,
sem_pid 成员保存了最近一次操作信号量的进程的pid。
sem_semval  成员保存着信号量的计数值。
sem_semncnt 成员保存着等待使用资源的进程数目。
sem_semzcnt  成员保存等待资源完全空闲的的进程数目。

2.semun
semun联合体在senctl()函数中使用,提供 senctl()操作所需要的信息。
union semun {
                 int val;                /* value for SETVAL */
                 struct semid_ds *buf;   /* buffer for IPC_STAT & IPC_SET */
                 ushort *array;          /* array for GETALL & SETALL */
                 struct seminfo *__buf;  /* buffer for IPC_INFO */
                 void *__pad;
};

3.sembuf
sembuf结构体被semop()函数用来定义对信号量对象的基本操作
struct sembuf {
         unsigned short  sem_num;        /* semaphore index in array */
         short           sem_op;         /* semaphore operation */
         short           sem_flg;        /* operation flags */
};
其中,
sem_num 成员为接受操作的信号量在信号量数组中的序号(数组下标)。
sem_op  成员定义了进行的操作(可以是正、负和零)。
sem_flg 是控制操作行为的标志。

4.semid_qs
和msgqid_ds类似,semid_qs结构被系统用来储存每个信号量对象的有关信息。
struct semid_ds {
      struct ipc_perm sem_perm;            /* permissions ..  see ipc.h */
      __kernel_time_t sem_otime;           /* last semop time */
      __kernel_time_t sem_ctime;           /* last change time */
      struct sem      *sem_base;           /* ptr to first semaphore in array */
      struct sem_queue *sem_pending;       /* pending operations to be processed */
      struct sem_queue **sem_pending_last; /* last pending operation */
      struct sem_undo *undo;               /* undo requests on this array */
      unsigned short  sem_nsems;           /* no.  of semaphores in array */
};

其中,
sem_perm  成员保存了信号量对象的存取权限以及其他一些信息。
sem_otime 成员保存了最近一次semop()操作的时间。
sem_ctime 成员保存了信号量对象最近一次改动发生的时间。
sem_base  指针保存着信号量数组的起始地址。
sem_pending 指针保存着还没有进行的操作。
sem_pending_last 指针保存着最后一个还没有进行的操作。
sem_undo  成员保存了 undo请求的数目。
sem_nsems 成员保存了信号量数组的成员数目。

有关的函数
1.semget()
使用semget()函数来建立新的信号量对象或者获取已有对象的标识符。
系统调用: semget()
函数声明: int semget(key_t key,int nsems,int semflg);
返回值:  semaphore set IPC identifier on success
int open_semaphore_set(key_t keyval, int numsems)
{
	int sid;
	if(!numsems) return(-1);
	if((sid=semget(keyval,numsems,IPC_CREAT|0660))==-1)
	{	
		return(-1);
	}
	return(sid);
}
函数的第二个参数 nsems 是信号量对象所特有的,它指定了新生成的信号量对象中信号量的数目,也就是信号量数组成员的个数。
2.semop()
使用这个函数来改变信号量对象中各个信号量的状态。
系统调用: semop()
函数声明: int semop(int semid, struct sembuf *sops, unsigned int nsops);
返回值:  0 on success (all operations performed)
第一个参数 semid是要操作的信号量对象的标识符。
第二个参数 sops是sembuf的数组,它定义了semop()函数所要进行的操作序列。
第三个参数 nsops保存着sops数组的长度,也即semop()函数将进行的操作个数。

3.semctl()
和消息队列的msgctl()函数类似,semctl()函数被用来直接对信号量对象进行控制
系统调用:  semctl()
函数声明:  int semctl(int semid, int semnum, int cmd, union semun arg);
返回值:   positive integer on success
比较一下这两个函数的参数我们回发现一些细微的差别。首先,因为信号量对象事实上是多个信息量的集合而非单一的个体,所以在进行操作时,不仅需要指定对象的标识符,还需要用信号量在集合中的序号来指定具体的信号量个体。

两个函数都有cmd参数, 指定了函数进行的具体操作。 不过,和msgctl()函数相比, semctl()函数可以进行的操作要多得多:
IPC_STAT  取得信号量对象的 semid_ds 结构信息,并将其储存在 arg 参数中 buf 指针所指内存中返回。
IPC_SET   用 arg 参数中 buf 的数据来设定信号量对象的的 semid_ds 结构信息。和消息队列对象一样,能被这个函数设定的只有少数几个参数。
IPC_RMID  从内存中删除信号量对象。
GETALL    取得信号量对象中所有信号量的值,并储存在 arg 参数中的 array 数组中返回。
GETNCNT   返回正在等待使用某个信号量所控制的资源的进程数目。
GETPID    返回最近一个对某个信号量调用semop()函数的进程的 pid。
GETVAL    返回对象那某个信号量的数值。
GETZCNT   返回正在等待某个信号量所控制资源被全部使用的进程数目。
SETALL    用 arg 参数中 array数组的值来设定对象内各个信号量的值。
SETVAL    用 arg 参数中val成员的值来设定对象内某个信号量的值。

int  get_sem_val(int sid, int semnum)
{
	return(semctl(sid,semnum,GETVAL,0));
}
上面的代码返回信号量对象中某个信号量的值。注意这里 semctl()函数的最后一个参数取的是零,这是因为执行 GETVAL 命令时这个参数被自动忽略了。
void init_semaphore(int sid, int semnum, int initval)
{
	union semun semopts;
        semopts.val=initval;
	semctl(sid, semnum, SETVAL, semopts);

}
上面的代码用 initval参数来设定信号量对象中某个信号量的值。
在消息队列和信号量对象中,都有 IPC_STAT 和 IPC_SET 的操作。但是由于传递参数的类型不同,造成了它们在使用上的差别。在 msgctl()函数中,IPC_STAT 操作只是简单的将内核内 msgqid_ds 结

构的地址赋予buf参数(是一个指针)。而在semctl()函数中, IPC_STAT操作是将 semid_ds 的内容拷贝到 arg 参数的 buf 成员指针所指的内存中。所以,下面的代码会产生错误,而 msgctl()函数的

类似代码却不会:
void getmode(int sid)
{
	int rc;
	union semun semopts;
	/*下面的语句会产生错误*/
	if((rc=semctl(sid, 0, IPC_STAT, semopts))==-1)
	{
		perror("semctl");
	}
	printf("Pemission Mode were %o\n", semopts.buf->sem_perm.mode);
	return;
}
为什么呢?因为实现没有给 buf 指针分配内存,其指向是不确定的。这种“不定向”的指针是 C 程序中最危险的陷阱之一。改正这个错误,只需要提前给 buf 指针准备一块内存。下面是修改过的代码:
void getmode(int sid)
{
	int rc;
	union semun semopts;
	struct semid_ds mysemds;
	/*给buf指针准备一块内存*/
	semopts.buf=&mysemds;

	/*现在OK了*/
	if((rc=semctl(sid, 0, IPC_STAT, semopts))==-1)
	{
		perror("semctl");
	}
	printf("Pemission Mode were %o\n", semopts.buf->sem_perm.mode);
	return;

}

实例:

semtool.c

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

#define SEM_RESOURCE_MAX 1  	/*Initial value of all semaphores*/

void opensem(int *sid, key_t key);
void createsem(int *sid, key_t key, int members);
void locksem(int sid, int member);
void unlocksem(int sid, int member);
void removesem(int sid);
unsigned short get_member_count(int sid);
int  getval(int sid, int member);
void dispval(int sid,int member);
void changemode(int sid, char *mode);
void usage(void);

int main(int argc, char *argv[])
{
	key_t key;
	int semset_id;
	if(argc == 1) usage();

	/*Create unique key via call to ftok()*/
	key=ftok(".",'s');	
	switch(tolower(argv[1][0]))
    	{

		case 'c': 
		if(argc!=3)usage();
		createsem(&semset_id, key, atoi(argv[2]));
		break;

		case 'l':
		if(argc!=3) usage();
		opensem(&semset_id, key);
		locksem(semset_id, atoi(argv[2]));
		break;
		
		case 'u':
		if(argc!=3) usage();
		opensem(&semset_id, key);
		unlocksem(semset_id, atoi(argv[2]));
		break;

		case 'd': 
		opensem(&semset_id,key);
		removesem(semset_id);
		break;

		case 'm':
		opensem(&semset_id, key);
		changemode(semset_id, argv[2]);
		break;

		default:
		usage();


	}
	return(0);

}

void opensem(int *sid,  key_t key)
{
	/*Open the semaphore set ---do not creat!*/
	if((*sid=semget(key, 0 , 0666)==-1)
	{

		printf("Semaphore set does not exist!\n");
		exit(1);
	}

}

void createsem(int *sid, key_t key, int members)
{
	int cntr;
	union semun semopts;
	if(members > SEMMSL){
		printf("Sorry,max number of semaphores in a set is %d\n",SEMMSL);
		exit(1);

	}
	printf("Attempting to create new semaphore set with %d members\n",members);
	if((*sid=semget(key, members, IPC_CREAT|IPC_EXCL|0666))==-1)
	{
		fprintf(stderr,"Semaphore set already exist!\n");
		exit(1);

	}
	
	semopts.val= SEM_RESOURCE_MAX;
	/*Initialize all members(could be done with SETALL)*/
	for(cntr=0;cntr<members;cntr++)
	{
		semctl(*sid, cntr, SETVAL, semopts);
	}

}


void locksem(int sid, int member)
{
	struct sembuf sem_lock={0, -1, IPC_NOWAIT};
	if(member <0 ||member>(get_member_count(sid) -1))
	{
		fprintf(stderr,"semaphore member %d out of range\n", member);
		return;
		

	}
	
	/*Attempt to lock the semphore set*/
        if(!getval(sid, member))
	{
		fprintf(stderr,"Semaphore resources exhausted (no lock)\n")
		exit(1);	
	}

	sem_lock.sem_num =member;
	
	if((semop(sid, &sem_lock, 1)==-1)
	{
		fprintf, "Lock faild\n");
       		exit(1);


	}
	else

		printf("Semaphore resources decremented by one (locked)\n");
	dispval(sid ,member);


}


void unlocksem(int sid, int member)
{

	struct sembuf sem_unlock={member, 1, IPC_NOWAIT};
	int semval;
        if(member<0 || member>(get_member_count(sid)-1))
	{
		fprintf(stderr,"Semaphore member %d out of range\n",member);
		return;

	}

	/*Is the semaphore set locked? */

	semval =getval(sid, member);
	if(semval==SEM_REOURSE_MAX){

		fprintf(stderr, "Semaphore not locked!\n");
		exit(1); 

	}

	sem_unlock.sem_num = member;

	/*Attempt to lock the semaphore set*/
        if((semop(sid, &sem_unlock, 1))==-1)
	{
		fprintf(stderr, "Unlock failed\n");
		exit

	}
   	else
		printf("Semaphore resources incremented by one(unlocked)\n");
        dispval(sid, member);


}


void removesem(int sid)
{
	semctl(sid, 0, IPC_RMID,0);
	print("Semaphore removed\n");

}

unsigned short get_member_count(int sid)
{

	union  semum semopts;
	struct semid_ds mysemds;
	
	semopts.buf= &mysemds;
	/*Return number of member in the semaphore set*/
	int rc;
	if((rc=semctl(sid, 0, IPC_STAT, semopts))==-1)
	{
		perror("semctl");
		return(-1)
	}
	
	return(semopts.buf->sem_nsems);

}

int getval(int sid, int member)
{
	int semval;
	semval= semctl(sid, member,GETVAL, 0)
	reval semval;

}

void changemode(int sid, char *mode)
{
	int rc;
	union semun semopts;
	struct semid_ds mysemds;
	
	/*Get current values for internal data structure */

	semopts.buf=&mysemds;
	rc = semctl(sid, 0, IPC_STAT, semopts);
	if(rc ==-1)
	{
		perror("semctl");
		exit(1);

	}

	printf("Old permission were %o\n", semopts.buf->perm.mode);

	/* Change the permission on the semaphore */
	sscanf(mode,"%ho",&semopts.buf->sem_perm.mode);

	/*Update the internal data structure */
	semctl(sid,0, IPC_SET, semopts);
	
	printf("Updated......\n");


}

void dispval(int sid, int member)
{
	int semval;
	semval= semctl(sid, member, GETVAL,0);
	printf("semval for member %d is %d\n", member ,semval);



}



void usage(void)
{
	fprintf(stderr, "semtool -Autility for thinking with semaphores\n");
	fprintf(stderr, "\nUSAGE:  semtool (c)reate <semcount>\n");
	fprintf(stderr, "                  (l)ock <sem #>\n");
     	fprintf(stderr, "                  (u)nlock <sem #>\n");
	fprintf(stderr, "                  (d)elete\n");
	fprintf(stderr, "                  (m)ode <mode>\n");
	exit(1);

}




分享到:
评论

相关推荐

    linux进程间通信——信号机制

    总结来说,Linux的信号机制是进程间通信的重要组成部分,它提供了简单但灵活的方式来处理系统事件和进程间的交互。`signal`和`sigaction`函数为程序员提供了处理信号的工具,使得我们可以根据需求定制信号的响应行为...

    进程同步——信号量机制

    关于信号量的文章,生产者消费者问题与读者写者问题---信号量机制,PV操作——进程同步的信号量问题,利用信号机制实现的 父子进程同步,嵌入式linux的学习笔记-进程间通信的信号与信号集(四)1)进程的同步与互斥 ...

    linux系统进程间通信——共享内存(System V版本)

    之前用过Prosix版本的共享内存和信号量,一直没有实践System V版本的,主要是因为其信号量集的概念操作有些复杂,今天试着写一个SV版本的共享内存进程间通信,使用信号量同步。程序提供了几个简单的用于操作SV版本...

    详解Linux进程间通信——使用信号量

    ### 详解Linux进程间通信——使用信号量 #### 一、什么是信号量 信号量是一种常用的同步机制,用于解决多进程或线程并发访问共享资源时可能出现的竞争条件问题。在多进程环境中,当多个进程尝试同时访问同一个共享...

    Linux进程间通信-信号量通信进程同步实例.pdf

    本文将深入探讨Linux系统中的一个特定IPC机制——信号量(Semaphore)及其在进程同步中的应用。信号量是一种用于控制多个进程对共享资源访问的同步工具,它能有效地防止竞态条件和死锁的发生。 信号量是一个整数值...

    linux进程通信

    ### Linux进程间通信——信号量详解 #### 一、引言 在Linux系统中,进程间的通信机制是实现多进程程序的关键技术之一。进程间通信(Inter-Process Communication,IPC)使得不同进程能够共享数据、协调操作顺序以及...

    进程间通信之套接字( socket )——完整代码

    进程间通信之套接字( socket ) 网络间通信 七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四.信号 ( sinal ) 五.消息队列 ( message queue ) 六.信号量 ( ...

    高级进程间通信问题——快速排序问题1

    【高级进程间通信问题——快速排序问题1】的实验是一个基于操作系统原理的编程挑战,旨在实现一个多线程或多进程的快速排序算法。这个实验在Ubuntu 18.04.5 LTS环境下进行,采用C/C++编程语言,并且允许使用C++11的...

    实验一-进程通信——管道和信号实验报告.doc

    - **进程间通信(IPC)**:进程之间传递数据的方式,包括管道、信号量、共享内存等。 - **Unix系统编程基础**:熟悉C语言编程、shell命令行操作等。 #### 实验方法 本实验采用Unix系统提供的API进行编程,实现进程...

    操作系统实验-信号量机制实现进程同步

    在多道程序设计中,进程同步是操作系统中的一个重要概念,它涉及到多个并发进程间的协调和通信,确保它们能正确、有序地访问共享资源,避免数据竞争和死锁等问题的发生。 信号量机制是实现进程同步的一种有效工具,...

    进程间通信之消息队列 ( message queue )——完整代码

    进程间通信之消息队列 ( message queue ) 消息队列是消息的链表,具有特定的格式,并由消息队列标识符标识. 七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四....

    Linux 多进程及其通信

    "Linux进程间通信——使用信号量.docx"可能涵盖了如何创建、操作和删除信号量,以及如何在实际应用中使用它们。 3. **共享内存**:共享内存允许两个或更多进程直接访问同一块内存区域,从而提供高效的通信。"Linux...

    进程间通信经典——银行柜员服务问题

    在IT领域,进程间通信(IPC,Inter-Process Communication)是多进程系统中不同进程之间交换数据的关键技术。尤其在Windows操作系统中,进程间通信扮演着至关重要的角色,使得不同进程能够协同工作,共享资源,解决...

    Linux进程间通信-信号通信信号发送实例.pdf

    在Linux操作系统中,进程间通信(IPC,Inter-Process Communication)是多个进程间协同工作、交换数据的关键机制。本文将重点讨论其中的一种通信方式——信号通信,并通过具体的信号发送实例来解析其工作原理。 ...

    Linux进程间通信-信号通信定时信号实例.pdf

    信号是Linux系统中进程间通信的一种轻量级方式,它能够用来中断进程的执行、改变进程状态或者通知进程发生了某些系统事件。在示例程序中,使用了`&lt;signal.h&gt;`头文件,这是处理信号的必备库。 定时信号通常与`alarm...

    MFC教程lesson 17-进程间通信.rar

    本教程聚焦于MFC中的一个重要概念——进程间通信(Inter-Process Communication,IPC)。进程间通信允许不同的进程之间交换数据,共享资源,协同工作,是Windows编程中的关键技能。Lesson 17的MFC教程将深入探讨这一...

    Linux进程间通信(一)——Sam用图概述

    总结,Linux进程间通信是多任务环境下实现进程协作的关键,通过管道、信号量、消息队列、共享内存和套接字等多种手段,开发者可以构建出复杂而灵活的系统。结合Sam的图解学习,可以更好地理解和应用这些技术。

    Linux进程间通信-信号通信实例.pdf

    信号是Linux系统中用于进程间通信的一种轻量级机制,它允许一个进程向另一个进程发送一个消息,告知接收进程发生了某种事件或需要执行某个动作。信号通信具有快速、简单的特点,但同时也存在信息量有限和同步能力较...

    Linux 下进程间通信实例

    Linux 下进程间通信实例之一——共享内存 使用了信号量用于进程间的同步

Global site tag (gtag.js) - Google Analytics