信号量和共享内存的配合使用

来源:互联网 发布:青蛙城 知乎 编辑:程序博客网 时间:2024/05/22 05:17

信号量

信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对临界资源访问的同步。
信号量的值大于或等于0时表示可供并发进程使用的资源实体数;小于0时代表正在等待使用临界资源的进程数

1、信号集的创建或打开

int semget(key_t key, int sems, int sem_flags);

key 是由ftok()的到的键值。 nsems指明要创建的信号集包含的信号个数,改参数后面还会提到。semflg为操作标志

  • IPC_CREATE:调用semget()时,会将本信号集中的key值和其他信号集中的key进行对比,如果存在相同的key,说明信号集已存在,此时返回该信号集的标识符,否则新建一个信号集并返回其标识符。
  • IPC_EXCL:该宏和IPC_CREATE一起使用,否则没有意义。当 semflg取PC_CREATE|IPC_EXCL时,表示如果发现信号集已经存在,则返回错误,错误码
    为EEXIST。

2、信号量的操作

int semop(int semid, struct sembuf *sops, size_t nsops);

semid为信号集的标识符;sops指向进行操作的结构体首地址;nsops指出将要进行操作的信号个数。semop函数调用成功返回0,失败返回-1。

struct sembuf{      short sem_num;//信号在信号集中的索引      short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,                    //一个是+1,即V(发送信号)操作。      short sem_flg;//操作标志IPC_NOWAIT,IPC_UNDO 详细请见UNIX高级环境编程}; 
取值范围 操作意义 sem_op > 0 信号机上sem_op的值,表示进程释放控制的资源 sem_op 如果没有设置IPC_NOWAIT则调用进程进入睡眠状态,直到信号值为0;否则不会睡眠,直接返回EAGAIN sem_op < 0 信号加上sem_op的值。若没有设置IPC_NOWAIT,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN

信号集的控制

int semctl(int semid, int semnum, int cmd, ... /*  union semun arg  */);

semid为信号集的标识符;semnum标识一个特定的信号;cmd指明控制操作的类型;“…”说明函数的参数时可选的

union semun{    int val;  /*for SETVAL*/    struct semid_ds *buf; /*for IPC_STAT and IPC_SET*/    unsigned short *array; /*for GETALL and SETALL*/};

cmd参数指定下列命令中的一种
这里写图片描述
这里写图片描述

共享内存

共享内存就是分配一块能被其他进程访问的内存。

共享内存区的创建

int shmget(key_t key, size_t size, int shmflg);
  • IPC_CREATE: 调用sheget时,系统将此值与其他所有共享内存区的key进行比较,如果存在相同的key,说明共享内存区已存在,此时返回该共享内存区的标识符,否则新建一个共享内存区并返回其标识符。

  • IPC_EXC: 该宏必须和IPC_CREATE 一起使用,否则没有意义。当shmflg取IPC_CREATE|IPC_EXCL时,表示如果发现信号集已经存在,则返回-1,错误码为EEXIST。

共享内存区的操作

在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。

void* shmat(int shmid, const void* shmaddr, int shmflag);

shmid为shmget函数的返回值;参数shmflg为存取权限标志;参数shmaddr为共享内存的附加点,其不同取值说明如下:
这里写图片描述
进程结束使用共享内存后,要通过shmdt断开与共享内存区的连接。

int shmdt(const void* shmaddr);

参数shmaddr为shmat函数的返回值。调用成功返回0,否则返回-1。

共享内存区的控制

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid为共享内存区的标识符;buf为指向shmid_ds结构体的指针;cmd为操作标志位
- IPC_RMID : 从系统中删除由shmid标识的共享内存区。
- IPC_SET : 设置共享内存区的shmid_ds结构。
- IPC_STAT : 读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址中。

应用实例

ipc.h

#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<sys/ipc.h>#include<sys/sem.h>#include<sys/shm.h>#include<sys/msg.h>#include<signal.h>union semun{    int val;    struct semid_ds *buf;    unsigned short *array;    struct seminfo *__buf;};key_t Ftok(const char *pathname, int proj_id){    key_t key = ftok(pathname, proj_id);    if(key == -1)    {        perror("ftok.");        exit(1);    }    return key;}int Semget(key_t key, int nsems, int semflg){    int id = semget(key, nsems, semflg);    if(id == -1)    {        perror("semget.");        exit(1);    }    return id;}int Shmget(key_t key, size_t size, int shmflg){    int id = shmget(key, size, shmflg);    if(id == -1)    {        perror("shmget.");        exit(1);    }    return id;}void* Shmat(int shmid, const void *shmaddr, int shmflg){    void *addr = shmat(shmid, shmaddr, shmflg);    if(addr == (void*)-1)    {        perror("shmat.");        exit(1);    }    return addr;}int Msgget(key_t key, int msgflg){    int id = msgget(key, msgflg);    if(id == -1)    {        perror("msgget.");        exit(1);    }    return id;}

cli.c

#include"ipc.h"void cli_exit(int signo){    printf("Client exit. . .\n");    exit(0);}// ./cli . 0xffint main(int argc, char *argv[]){    struct sigaction act;    act.sa_handler = cli_exit;    key_t shm_key = Ftok(argv[1], atoi(argv[2]));    int shm_id = Shmget(shm_key, 0, 0);    char *addr = (char*)Shmat(shm_id, NULL, 0);    int sem_id = Semget(shm_key, 0, 0);    struct sembuf v = {1, 1, SEM_UNDO};    struct sembuf p = {0, -1, SEM_UNDO};    sigaction(SIGINT, &act, NULL);    while(1)    {        semop(sem_id, &p, 1);        printf("Ser:>%s\n",addr);        printf("Cli:>");        scanf("%s",addr);        semop(sem_id, &v, 1);    }    return 0;}

ser.c

#include"ipc.h"int shm_id, sem_id;char *addr;void ser_exit(int signo){    semctl(sem_id, 0, IPC_RMID);    semctl(sem_id, 1, IPC_RMID);    shmdt(addr);    shmctl(shm_id, IPC_RMID, NULL);    printf("Server exit . . .\n");    exit(0);}// ./ser . 0xffint main(int argc, char *argv[]){    struct sigaction act;    act.sa_handler = ser_exit;    key_t shm_key = Ftok(argv[1], atoi(argv[2]));    shm_id = Shmget(shm_key, 1024, IPC_CREAT|IPC_EXCL|0755);    addr = (char*)Shmat(shm_id, NULL, 0);    sem_id = Semget(shm_key, 2, IPC_CREAT|IPC_EXCL|0755);    union semun init;    init.val = 0;    semctl(sem_id, 0, SETVAL, init);    semctl(sem_id, 1, SETVAL, init);    struct sembuf v = {0, 1, SEM_UNDO};   //第一个参数代表0标志位,第二个是v操作后加的值    struct sembuf p = {1, -1, SEM_UNDO};    //第三个参数分为IPC_NOWAIT和SEM_UNDO    sigaction(SIGINT, &act, NULL);    while(1)    {        printf("Ser:>");        scanf("%s",addr);        semop(sem_id, &v, 1);        semop(sem_id, &p, 1);        printf("Cli:>%s\n",addr);    }    return 0;}

运行示例
server端
这里写图片描述
client端
这里写图片描述

原创粉丝点击