LINUX_C编程实战—第十章《进程间的通信》-信号量

好久没更新博客啦,今天工作闲了可以做一个终结啦。


System V信号量的概念:可以理解为一个计数器,主要用于对临界资源的访问,进行PV操作(P操作,获取资源,资源数-1;V操作释放资源,资源数+1);信号量的值大于或等于0时表示可供并发进程使用的资源实体数,小于0表示正在等待使用临界资源的进程数。一般用的最多的为二进制信号量,既值取0,1;

键值:可以理解为IPC的一个中间介值,用来进行通信的,由ftok函数创建;

key_t ftok( const char *pathname, int proj_id); //文件的设备编号和节点

                             函数调用成功返回key_t键值,出错返回-1;其中在unix系统中,proj_id是子序列,为一个8bit的整数,范围为0-255;

     此外要特别注意当pathname指向的文件或目录被删除而且又重新创建时,可以正常创建键值,但键值确和之前的值不一样啦,程序运行不会报错,但无法达到传输通信的作用。确保键值不变,要么保证ftok的文件不被删除,要么不用ftok,指定一个固定的key值

  一、信号量的创建:

                                         #include<sys/sem.h>

int semget( key_t key, int nsems, int semflg);//执行成功返回信号量标识符,失败返回-1;

                                     nsems:信号量集中包含的信号量个数;

     semflg:操作标识,IPC_CREATE | IPC_EXCL(信号集已有则返回错误)

二、信号量的操作(+-1)

int semop( int semid, struct sembuf *sops, size_t nsops);//成功返回0,失败返回-1;

semid;信号集标识符;

struct sembuf {

ushort sem_num;//信号量在信号量集中的索引

        short sem_op;//操作类型,+1(释放资源,资源数加1)  or  -1(使用资源,资源数减少)  

short sem_flg;//操作标志,IPC_NOWAIT  或 0;

}

nsops:将要进行操作的信号量个数,>=1

三、信号集控制: 对信号集中的信号量赋值、取值、删除等;

int semctl( int semid, int semnum,int cmd, union semun);//成功返回0,失败返回-1;

semid:信号量集标识符

semnum:操作信号量在信号量集中的编号,第一个信号量的标号为0;

cmd:取值可为SETVAL、GETVAL、IPC_RMID等;

union semun{

int val;

struct semid_ds *buf;

ushort *array;

struct seminfo *buf;

void *pad;

}

测试代码如下:实现进程间的同步

头文件:sem.h

#ifndef _SEM_H_
#define _SEM_H_

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

#define PATHNAME "."
#define NUMBER 1

int creat_sem(int sems);

int init_sem(int semid,int which,int value);

int get_sem();

int p_sem(int semid,int which,int *flag);

int v_sem(int semid,int which);

int destroy(int semid);

#endif 

sem.c文件

#include "sem.h"

/* 创建信号量集*/
int creat_sem(int sems)
{
	key_t key;
	int semid;
	/* 利用ftok创建键值*/
	key=ftok(PATHNAME,NUMBER);
	if(key < 0)
	{
		perror("ftok error");
		exit(1);
	}
	/* 创建信号量集函数*/
	semid=semget(key,sems,IPC_CREAT | 0666);
	if(semid < 0)
	{
		perror("sem_get error");
		exit(1);
	}
	
	return semid;
}
int get_sem()
{
	/* 利用IPC_CREAT打开已创建的信号量集*/
	return creat_sem(2);
}
/* 不懂为啥还要重复定义共同体,不定义就找不到定义的参数argc*/
union semun
{
    int val; // value for SETVAL
    struct semid_ds *buf; // buffer for IPC_STAT & IPC_SET
    unsigned short *array; // buffer for GETALL & SELALL
    struct seminfo * __buf; // buffer for IPC_INFO
};

/* 初始化信号集*/
int init_sem(int semid,int which,int value)
{
	union semun argc;
	int ret;
	argc.val = value;
	/* semctl函数用于初始化编号为which的信号集;亦可用于删除*/
	ret = semctl(semid,which,SETVAL,argc);
	if(ret < 0)
	{
		perror("semctl error");
		exit(1);
	}

	return 0;
}

/* P操作获取资源,资源数-1 */
int p_sem(int semid,int which,int *flag)
{
	struct sembuf sbuf={which,-1,0};
	int ret;

	ret = semop(semid,&sbuf,1);
	if(ret < 0)
	{
		perror("p_sem error");
		/* 出错标志位*/
		*flag = 1;
		exit(0);
	}
	return 0;

}

/* V操作释放资源,资源数+1 */
int v_sem(int semid,int which)
{
	struct sembuf sbuf={which,1,0};
	int ret;

	ret = semop(semid,&sbuf,1);
	if(ret < 0)
	{
		perror("v_sem error");
		exit(0);
	}
	return 0;
}

/* 清空信号集*/
int destroy(int semid)
{
	int ret;
	/* 使用参数IPC_RMID删除信号集*/
	ret = semctl(semid,0,IPC_RMID,0);
	if(ret < 0)
	{
		perror("semctl error");
		exit(1);
	}
	return 0;
}
主函数:

#include "sem.h"

int main(int argc,char **argv)
{
	pid_t pid;
	int semid,sem_id;
	
	/* 创建键值 */
	semid = creat_sem(10);
	
	init_sem(semid,0,1);
	pid = fork();
	
	if(pid < 0)
	{
		perror("fork error");
		exit(0);
	}
	else if(pid == 0)
	{
		/* 获得键值*/
		sem_id = get_sem();
		//printf("sem_id=%d\n",sem_id);
		while(1)
		{
			p_sem(sem_id,0);
			
			printf("i am child\n");
			printf("child_flag=%d\n",_flag);
			printf("c_hello ");
			sleep(2);
			printf("c_world!\n");
			sleep(2);
			
			v_sem(sem_id,0);
	 	}
	
	}
	else
	{
		while(1)
		{
			/* 获得信号量集中编号为0的资源*/
			p_sem(semid,0);
			
	/*************临界区 *****************************************************/		
			printf("i am father\n");
			printf("father_flag=%d\n",_flag);
			printf("f_creat ");
			sleep(2);
			printf("sucess!\n");
			sleep(2);
	/*************临界区 *****************************************************/			
			
			/* 释放资源*/
			v_sem(semid,0);
		
		}
	wait(0);
	
	}
	destroy(semid);
	return 0;

}

makefille文件:

all:main clear
main:sem.c main.c
	gcc sem.c main.c -o main

clear:
	rm -rf *.o
运行结果:

猜你喜欢

转载自blog.csdn.net/caozhigang129/article/details/78460516