一、信号量集函数
1.1、semget
-
功能:用来创建或打开一个信号量集;
-
原型:int semget(key_t key, int nsems, int semflg);
-
key表示信号集的名字,一般由ftok函数产生;
-
nsems 表示信号集中信号量的个数;
-
semflg 用来标识信号量集合的权限,和创建文件类似,由九个权限标志为构成如0644,他还可以和以下参数一起使用:
IPC_CREAT 表示如果key不存在就创建; IPC_EXCL 表示如果key存在就返回失败; IPC_NOWAIT 表示如果需要等待,则直接返回错误;
-
返回值:成功返回一个非负整数即该信号量的标识码;失败返回-1;
1.2、semctl
-
功能:用来控制信号量集;
-
原型:int semctl(int semid, int semnum, int cmd, …);
-
semid由semget返回的信号量标识;
-
semnum指定信号集中信号量的序号;
-
cmd表示将要采取的操作;cmd可采取的操作有:IPC_RMID、IPC_SET、IPC_STAT和IPC_INFO,定义在:/usr/include/linux/ipc.h中;
——IPC_RMID表示删除信号集 ——IPC_STAT表示获取ipc_perm的参数 ——IPC_INFO表示获取系统信息 ——IPC_SET表示设置ipc_prem的参数,对于这个参数,semctl有单独的规定参数: ——GETPID获取信号量拥有者的pid的值; ——GETVAL获取信号量的值; ——GETALL获取所有信号量的值; ——GETNCNT获取等待信号量的值递增的进程数; ——GETZCNT获取等待信号量的值递减的进程数; ——SETVAL设置信号量的值; ——SETALL设置所有信号的值;
-
最后一个参数根据命令不同而不同,他是一个类型为senum的联合体,不操作结构体则为0
union semun{ int value; struct semid_ds *buf; unsigned short *array; struct seminfo *_buf; }
-
返回值 成功返回0,失败返回-1
1.3、semop
- 功能:修改集合中一个或多个信号量值;
- 原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
- semid信号量标识码;
- nsops标识信号量个数;
- 返回值:成功返回0;失败返回-1;
- sops是一个sembuf类型的指针;
struct sembuf{
short sem_num;//信号量名称
short sem_op;//计数值操作-1,1
short sem_flg;//选项,默认为0,表示计数值小于减去的数,便会阻塞
}
——sem_num标识信号量编号;
——sem_op信号量一次pv操作时加减的数值,一般会用到两个值:
-1,p操作,等待信号量变得可用
+1,v操作,发出的信号量变得可用
——sem_flg操作标识,有以下值: IPC_NOWAIT和SEM_UNDO
IPC_NOWAIT对某一信号量操作,即使其中一个操作失败,也不会导致修改其他信号量的值;
SEM_UNDO当进程退出后,该进程对sem进行的操作将被撤销;
二、两进程读写同步练习
/* 信号灯集+共享内存*/
/*
要求:父子进程通过SystemV信号灯集同步对共享内存的读写
1.父进程从键盘输入字符串到共享内存
2.子进程删除字符串中的空格并打印
3.父进程输入quit后删除共享内存和信号灯集,程序结束
思路:
实现内存(缓冲区)读写同步机制+互斥机制,需要2个信号灯,一个为读,一个为写。
1.创建共享内存
2.创建信号灯
3.fork进程
4.父进程阻塞等待键盘输入
4..阻塞等待P写资源
4...写入共享内存
4....判断是否结束,如果是则kill子进程
4.....如果否,则V读资源
5.子进程阻塞等待P读资源
5..读取共享内存
5...V写资源
5....打印输出
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <wait.h>
#include <string.h>
#define SEM_WRITE 0 //写信号灯编号
#define SEM_READ 1
#define P_OPERATION -1 //systemV-P操作
#define V_OPERATION 1 //V操作
typedef union semunion{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
}semunion;
/*
func:对num个信号灯赋值
para:
semid 信号灯集id
sema_value[] 信号灯值缓冲区
num 信号灯个数
*/
void init_sema_value(int semid,int *sema_value,int num){
semunion semu;
for(int i=0;i<num;i++){
semu.val=sema_value[i];
semctl(semid,i,SETVAL,semu);
}
}
/*
func:P/V操作封装
para:
*/
void operate(int semid,int whichnum,int op){
struct sembuf opbuf;
opbuf.sem_num=whichnum;
opbuf.sem_op=op;
opbuf.sem_flg=0;
semop(semid,&opbuf,1);
}
int main(void)
{
int shmid,semid,sema_value[2];
pid_t pid;
char *addr=NULL;
key_t Key;
Key=ftok(".",'m');
//创建共享内存
if((shmid=shmget(Key,1024,IPC_CREAT|0666))<0){
perror("shmget");
exit(-1);
}
//创建信号灯集
if((semid=semget(Key,2,IPC_CREAT|0666|IPC_EXCL))<0){
perror("semget");
goto error1;
}
//信号灯初始化
sema_value[SEM_WRITE]=1;
sema_value[SEM_READ]=0;
init_sema_value(semid,sema_value,2);
//映射共享内存
if((addr=(char *)shmat(shmid,NULL,0))<0){
perror("shmat");
goto error2;
}
if((pid=fork())==0){
char *new,*old;
while(1) //子进程循环
{
operate(semid,SEM_READ,P_OPERATION); //对读信号灯的P操作
new=old=addr;
while(*old){
if((*old)!=' '){
(*new++)=(*old);
}
old++;
}
(*new)='\0';
//if(strcmp(addr,"quit\n")==0)exit(0);
printf("your input is : %s\n",addr);
operate(semid,SEM_WRITE,V_OPERATION); //对写信号灯的V操作
}
}
else
{
while(1) //父进程
{
operate(semid,SEM_WRITE,P_OPERATION);
printf("please input something:\n");
fgets(addr,1024,stdin);
if(strcmp(addr,"quit\n")==0)
{
kill(pid,SIGUSR1);
//waitpid(pid,NULL,0);
break;
}
operate(semid,SEM_READ,V_OPERATION);
}
}
error2:
semctl(semid,0,IPC_RMID); //删除信号灯集
shmdt(addr); //撤销共享内存映射
error1:
shmctl(shmid,IPC_RMID,NULL); //删除共享内存
return 0;
}