进程间通信之SystemV IPC--共享内存和信号量

共享内存:最快的可用IPC形式,一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传输就不再涉及内核。然而往该共享内存区存放信息或从中取走信息的进程间通常需要某种形式的同步。(不涉及内核:进程不是通过执行任何进入内核的系统调用来传递彼此的数据)。

在消息队列处实现过一个服务器和客户机通信的例子:简单的客户端---服务器例程

下图展示了从服务器到客户机的文件数据流向:


当我们使用共享内存区对对文件进行拷贝时,文件数据的流向如下图:


图中的数据之拷贝了两次,即只涉及了两次内核访问:一次从输出文件到共享内存,一次从共享内存区到输出文件。

创建和打开共享内存:

int shmget(key_t key, size_t size, int shmflg);             //size--分配共享内存段的大小(N * 4K)        shmflg--创建(IPC_CREAT|0644)已存在(置0)

返回值:成功返回共享存储区ID,出错返回-1

说明: 参数size是该共享存储段的长度。实现通常将其向上取为系统页长的整数倍。但是,若指定的size值并非系统页长的整数倍,那么最后一页的余下部分是不可用的。如果正在创建一个新段(一般是在服务器中),则必须指定其size。如果正在引用一个现存的段(一个客户进程),则将size指定为0,当创建一衅端时,段内的内容初始化为0

删除共享内存:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);//cmd--置为IPC_RMID,          buf---不关心(置0)

返回值:成功返回0,出错返回-1

将共享内存挂载到自己的内存空间中:

void *shmat(int shmid, const void *shmaddr, int shmflg);             //shmaddr--一般置为NULL,    shmflg-- 一般置为0

返回值:  成功返回挂载的内存空间的起始地址,出错返回-1

卸载共享内存:

 int shmdt(const void *shmaddr);                  //形参为shmat函数的返回地址

返回值:成功返回0,出错返回-1

案例程序:分别实现读写进程对共享内存操作

创建共享内存:

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/ipc.h>

int main()
{
    int num = 10;

    int id  = shmget(ftok(".", 'a'), sizeof(int), IPC_CREAT|0644);
    if(-1 == id)
    {
        perror("shmget");
        exit(1);
    }
    
    printf("creat ok\n");
    return 0;
}

读进程:

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/ipc.h>

int main()
{
    int id  = shmget(ftok(".", 'a'), sizeof(int), IPC_CREAT|0644);
    if(-1 == id)
    {
        perror("shmget");
        exit(1);
    }
    
    int *p = (int*)shmat(id, NULL, 0);
    while(1)
    {
        printf("read = %d\n", *p);
        usleep(1000);
    }
    return 0;
}

写进程:

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/ipc.h>

int main()
{
    int id  = shmget(ftok(".", 'a'), sizeof(int), IPC_CREAT|0644);
    if(-1 == id)
    {
        perror("shmget");
        exit(1);
    }
    
    int *p = (int*)shmat(id, NULL, 0);
    int i = 1;
    while(1)
    {
        *p = i++;
        printf("%d write ok\n", i);
        sleep(1);
    }
    return 0;
}

删除共享内存:

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/ipc.h>

int main()
{
    int id = shmget(ftok(".", 'a'), sizeof(int), 0);
    if(-1 == id)    perror("shmget"), exit(1);

    if(-1 == shmctl(id, IPC_RMID,0))
    {
        perror("shmctl");
        exit(1);
    }
    return 0;
}
Makefile:
.PHONY: clean all

all : rd wr ctl cr

cl=rd wr ctl cr

rd : read.o
	cc $^ -o $@
wr : write.o
	cc $^ -o $@
ctl : shmctl.o
	cc $^ -o $@
cr : creat.o
	cc $^ -o $@
%.o:%.c
	cc -c $^ -o $@

clean:
	rm -rf *.o $(cl)
信号量:信号量是一个计数器,用于多进程对共享数据对象的访问。即主要用于解决同步和互斥问题。信号量解决同步和互斥原语

信号量集的创建:

int semget(key_t key, int nsems, int semflg);                  //nsems---信号集中有几个信号量(默认为1,已存在打开时置0)      semflg--- 创建IPC_CREAT|0644   存在(置0)

返回值:成功返回信号量ID,出错返回-1

信号量集的操作:设置信号量初值

union semun {                                      //设置初值时只用到val
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};
int semctl(int semid, int semnum, int cmd, ...);                   //semnum---第几个信号量0,1,2……               cmd---SETVAL            semnum的值

返回值:成员semun的val的值

信号量集的操作:得到信号量初值

int semctl(int semid, int semnum, int cmd);                   //semnum---第几个信号量0,1,2……               cmd---GETVAL  

返回值:成员semun的val的值

实现PV操作:

struct sembuf{
                unsigned short sem_num;	                      //要操作第几个信号量
                short 	sem_op;	                              //  -1     或     1
                short   sem_flg;                              //   0   指定IPC_NOWAIT(出错返回EAGAIN)
};
int semop(int semid, struct sembuf *sops, unsigned nsops);        //sops---信号量操作数组起始地址            nops规定数组的大小

返回值:成功返回0,出错返回-1

PV操作保护重要断码段:


案例程序:

创建信号量集并设置初值:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

union semun
{
    int value;
};

int main()
{
    int num;
    union semun su;
    int id = semget(123, 1, IPC_CREAT|0644);
    if(-1 == id) perror("semget"), exit(1);
    
    printf("设置初值:\n");
    scanf("%d", &num);
    su.value = num;
    if(-1 == semctl(id, 0, SETVAL, su))
    {
        perror("semctl");
        exit(1);
    }

    return 0;
}
P操作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void p(int id)
{
    struct sembuf sb[1];
    sb[0].sem_num = 0;
    sb[0].sem_op = -1;
    sb[0].sem_flg = 0;
    if(semop(id, sb, 1) == -1)
    {
        perror("semop");
            exit(1);
    }   
}

int main()
{
    int id = semget(123, 0, 0);
    if(-1 == id) perror("semget"), exit(1);

    p(id);

    return 0;
}
V操作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void v(int id)
{
    struct sembuf sb[1];
    sb[0].sem_num = 0;
    sb[0].sem_op = 1;
    sb[0].sem_flg = 0;
    if(semop(id, sb, 1) == -1)
    {
        perror("semop");
            exit(1);
    }   
}

int main()
{
    int id = semget(123, 0, 0);
    if(-1 == id) perror("semget"), exit(1);

    v(id);

    return 0;
}
获得信号量值:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int main()
{
    int id = semget(123, 0, 0);
   if(-1 == id)
   {
       perror("semget");
       exit(1);
   }
    int ret = semctl(id, 0, GETVAL);
    printf("ret = %d\n", ret);

    return 0;
}
删除信号量集:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>


int main()
{
    int id = semget(123, 1, IPC_CREAT|0644);
    if(-1 == id) perror("semget"), exit(1);
    
    if(-1 == semctl(id, 0, IPC_RMID))
    {
        perror("semctl");
        exit(1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bian_cheng_ru_men/article/details/80160659