`
tomotoboy
  • 浏览: 162284 次
  • 性别: 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);

}




分享到:
评论

相关推荐

    进程同步——信号量机制

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

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

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

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

    主要介绍了详解Linux进程间通信——使用信号量,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

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

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

    Linux 下进程间通信实例

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

    进程间通信机制的分析与比较

    进程在核心的协调下进行相互间的通讯机制——管道,信号量,消息队列。

    详解Linux进程间通信——使用共享内存

    进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问...

    《UNIX网络编程 第2版. 第2卷, 进程间通信(中文版)》(W·Richard Stevens[美] 著)

    良好的进程间通信(IPC)机制是提高UNIX程序性能的关键。本书全面深入地讲解了各种进程间通信形式,包括消息传递、同步、共享内存及远程调用(RPC)。书中包含了大量经过优化的源代码,帮助读者加深理解。这些源代码...

    《Visual C++范例大全》随书光盘 第十一章

    实例268——使用信号量(Semaphore)实现线程的同步 实例269——使用多线程进行文件搜索 实例270——获取当前系统的所有进程 实例271——实现应用程序在系统中只能运行一个实例 实例272——获取所有打开窗口...

    linux源代码分析——消息管理

    在操作系统中,有些进程存在着相互制约的关系,这些制约关系来源于并行... 这种机制就叫做进程间通信,或IPC.在linux 中支持UNIX SYSTEM V 的三种通信机制: 消息队列, 信号量和共享内存. 现就消息队列这种机制进行分析.

    Visual C++实践与提高——串口通信与工程应用篇1

    4.4.3 使用信号量(Semaphore) 84 4.4.4 使用事件(Event) 84 4.4.5 各种方法的比较 85 4.5 多线程串口程序设计 85 4.5.1 多线程的应用框架 85 4.5.2 相关的声明 86 4.5.3 创建线程 87 4.5.4 编写线程函数 87 ...

    Linux内核源代码情景分析 (上下册 高清非扫描 )

    第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用...

    linux内核源代码情景分析

    第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用...

    操作系统重点

     (3)各相关进程间通过什么信号量实现彼此的制约,标明信号量的含义和初值;  (4)用P、V操作写出相应的代码段;  (5)验证代码的正确性:设以不同的次序运行各进程,是否能保证问题的圆满解决。切忌按固定顺序执行...

    linux 内核源代码分析

    第7章 基于socket的进程间通信 7.1 系统调用socket() 7.2 函数sys—socket()——创建插口 7.3 函数sys—bind()——指定插口地址 7.4 函数sys—listen()——设定server插口 7.5 函数sys—accept()——接受...

    嵌入式Linux应用程序开发标准教程(第2版全)

    接着系统地讲解了嵌入式Linux的环境搭建,以及嵌入式Linux的I/O与文件系统的开发、进程控制开发、进程间通信开发、网络应用开发、基于中断的开发、设备驱动程序的开发以及嵌入式图形界面的开发等,并且还安排了丰富...

    Linux内核情景分析(非扫描版)

    第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6. 5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 《LINUX内核源代码情景分析(下册)》图书...

    Linux内核情景分析

    第 7章 基于socket的进程间通信 7.1 系统调用socket() 7.2 函数sys—socket()——创建插口 7.3 函数sys—bind()——指定插口地址 7.4 函数sys—listen()——设定server插口 7.5 函数sys—accept()——接受连接...

Global site tag (gtag.js) - Google Analytics