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

来源:互联网 发布:大数据平台需求分析 编辑:程序博客网 时间:2024/06/07 13:07

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


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 1int 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 clearmain:sem.c main.cgcc sem.c main.c -o mainclear:rm -rf *.o
运行结果: