好久没更新博客啦,今天工作闲了可以做一个终结啦。
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
运行结果: