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

信号量

信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对临界资源访问的同步。
信号量的值大于或等于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参数指定下列命令中的一种
信号量和共享内存的配合使用_第1张图片
信号量和共享内存的配合使用_第2张图片

共享内存

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

共享内存区的创建

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为共享内存的附加点,其不同取值说明如下:
信号量和共享内存的配合使用_第3张图片
进程结束使用共享内存后,要通过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
#include
#include
#include
#include
#include
#include
#include
#include

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 . 0xff
int 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 . 0xff
int 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端
信号量和共享内存的配合使用_第4张图片
client端
信号量和共享内存的配合使用_第5张图片

你可能感兴趣的:(linux编程实践,服务器)