Linux进程间通信(IPC) ---- 共享内存

目录

 

共享内存

定义

共享内存的同步机制

 shmget函数

shmat函数

shmdt函数

shmctl函数

不使用信号量对共享内存进行同步操作

使用信号量对共享内存进行同步操作


共享内存

定义

共享内存允许两个不相关的进程访问同一块逻辑内存。因为数据不需要在进程之间复制,所以共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。大多数共享内存的具体实现,都把由不同进程之间共享的内存安排为同一段物理内存。共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中。其它进程可以将同一段共享内存连接到它们自己的地址空间中。所以进程都可以访问共享内存中的地址,就像是每个进程中malloc出的地址一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。

共享内存的同步机制

共享内存为在多个进程间共享和传递数据提供了一种有效方式,但并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问,一般用共享内存来提供对大块内存区域的有效访问。

未提供同步机制表现在,如果在第一个进程结束对共享内存的写操作之前,并没有自动的机制可以阻止第二个进程对共享内存进行读取。所以通常,信号量被用来实现对共享内存访问的同步。同步之后使得,如果一个进程正在将数据放入共享内存,则在它做完写操作之前,别的进程不能去取这些数据。

Linux进程间通信(IPC) ---- 共享内存_第1张图片

//共享内存使用的函数类似于信号量的函数,定义如下:
#include

void *shmat(int shm_id,const void* shm_addr,int shmflg);
int shmctl(int shm_id,int cmd,struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key,size_t size,int shmflg);

 shmget函数

用shmget函数来创建一个新的共享内存,或者获取一个已有的共享内存。

void *shmat(int shm_id,const void* shm_addr,int shmflg);

①与信号量一样,程序需要提供一个参数key,它有效地为共享内存段命名。特殊的键值IPC_PRIVATE,它用来创建一个只属于创建进程的共享内存。

②size以字节为单位指定需要共享的内存容量。

③shmflg为权限标志,与创建文件时使用的mode标准一样。权限标志对共享内存非常有用,因为允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其他进程只能读取该共享内存。可以利用这种功能来提供一种有效的对数据进行只读访问的方法,通过将数据放入共享内存并设置它的权限,就可以避免数据被其他用户修改。

共享内存创建成功,shmget函数返回一个共享内存标识符(非负整数),这个标识符将用于后续的共享内存函数。

shmat函数

用shmget函数将共享内存映射到进程地址空间。

void *shmat(int shm_id,const void* shm_addr,int shmflg);

①shm_id是shmget返回的共享内存标识符。

②shm_addr指定共享内存连接到当前进程中的地址位置。通常是一个空指针,表示让系统选择共享内存出现的地址。

③shmflg是一组位标志。两个可能取值为:SHM_RND(这个标志与shm_addr联合使用,用来控制共享内存连接的地址) 和 SHM_RDONLY(它使得连接的内存只读)。

shmflg & SHM_RDONLY 为true时,此时即使该共享内存的访问权限允许写操作,它都不能被写入。

shmat函数调用成功,返回一个指向共享内存第一个字节的指针;如果失败,返回-1。

shmdt函数

shmdt函数是将共享内存从当前进程中分离,即断开映射

int shmdt(const void *shm_addr);

参数shm_addr是shmat返回的地址指针。调用成功返回0,失败返回-1。

注意:将共享内存只是分离,并未删除它,只是使得该共享内存对当前进程不再可用。

shmctl函数

shmctl为控制函数

int shmctl(int shm_id,int command,struct shmid_ds *buf);

shmid_ds结构体至少包含以下成员:

struct shmid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};

①shm_id是shmget返回的共享内存标识符。

②command是要采取的动作,它可以取3个值。

            命       令                                                说                    明
        IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值
        IPC_SET 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
        IPC_RMID 删除共享内存段

③buf是一个指针,指向包含共享内存模式和访问权限的结构。

shmctl函数成功时返回0,失败时返回-1。

不使用信号量对共享内存进行同步操作

/*   a.c   */
#include
#include
#include
#include
#include
#include
#include"sem.h"

int main()
{
    int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);//创建共享内存
    assert(shmid != -1);

    char *s = (char*)shmat(shmid,NULL,0);//将共享内存映射到地址空间

    while(1)
    {
        char buff[128] = {0};
        printf("input:\n");
        fgets(buff,128,stdin);//循环输入,相当于对共享内存进行写操作

        strcpy(s,buff);//buff --> s
        
        if(strncmp(buff,"end",3) == 0)
        {
            break;
        }
    }

    shmdt(s);//断开映射

    exit(0);
}
#include
#include
#include
#include
#include
#include

int main()
{
    int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);
    assert(shmid != -1);

    char *s = (char*)shmat(shmid,NULL,0);
    assert(s != NULL);

    while(1)
    {
        if(strncmp(s,"end",3) == 0)
        {
            break;
        }

        printf("s=%s\n",s);//打印读取到共享内存中的值

        sleep(1);
    }

    shmdt(s);//断开映射
    
    if(shmctl(shmid,IPC_RMID,NULL) == -1)
     {
         perror("shmctl error");
     }

     exit(0);
}

Linux进程间通信(IPC) ---- 共享内存_第2张图片

没有同步操作时运行结果如上图,可以分析出进程a每写一次,共享内存已经被进程b访问了多次。

使用信号量对共享内存进行同步操作

/*   sem.h   */
#include
#include
#include
#include
#include
#include

void sem_init();
void sem_p();
void sem_v();
void sem_destroy();

union semun
{
    int val;
};
#include"sem.h"
#include
#include
#include
#include
#include
#include


static int semid;
void sem_init()
{
    semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//创建一个新的信号量
    if(semid == -1)//创建失败
    {
        semid = semget((key_t)1234,1,IPC_CREAT|0600);//获取这个键值对应的已有的信号量
        if(semid == -1)//获取失败
        {
            perror("semget error");
            return;
        }
    }
    else
    {
        union semun a;//semun --> semctl
        a.val = 1;

        if(semctl(semid,0,SETVAL,a) == -1)//给信号量赋初始值
        {
            perror("semctl error");
        }
    }
}

void sem_p()
{
    struct sembuf buf;//sembuf ---> semget
    buf.sem_num = 0;//第一个信号
    buf.sem_op = -1;//p操作,信号量-1
    buf.sem_flg = SEM_UNDO;

    if(semop(semid,&buf,1) == -1)
    {
        perror("semop p error");
    }
}

void sem_v()
{

    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;//v操作,信号量+1
    buf.sem_flg = SEM_UNDO;

    if(semop(semid,&buf,1) == -1)
    {
        perror("semop v error");
    }
}

void sem_destroy()
{
    if(semctl(semid,0,IPC_RMID) == -1)//IPC_RMID删除一个已经无需继续使用的信号量标识符
    {
        perror("semctl error");
    }
}
#include
#include
#include
#include
#include
#include
#include"sem.h"

int main()
{
    int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);//创建共享内存
    assert(shmid != -1);

    char *s = (char*)shmat(shmid,NULL,0);
    sem_init();

    while(1)
    {
        char buff[128] = {0};
        printf("input:\n");
        fgets(buff,128,stdin);

        sem_p(0);//p(s1)

        strcpy(s,buff);

        sem_v(1);//v(s2)
        
        if(strncmp(buff,"end",3) == 0)
        {
            break;
        }
    }
    shmdt(s);

    exit(0);
}
#include
#include
#include
#include
#include
#include

int main()
{
    int shmid = shmget((key_t)1234,256,IPC_CREAT|0600);
    assert(shmid != -1);

    char *s = (char*)shmat(shmid,NULL,0);
    assert(s != NULL);
    
    sem_init();

    while(1)
    {
        sem_p(1);//p(s2)
        if(strncmp(s,"end",3) == 0)
        {
            break;
        }

        printf("s=%s\n",s);

        sem_v(0);//v(s1);

        sleep(1);
    }

    shmdt(s);

    sem_destroy();//destory semaphore
    
    if(shmctl(shmid,IPC_RMID,NULL) == -1)//删除共享内存
     {
         perror("shmctl error");
     }

     exit(0);
}

Linux进程间通信(IPC) ---- 共享内存_第3张图片

使用信号量对共享内存进行同步操作之后,使得每次对共享内存的访问都是在当前进程写完之后,在它做完写操作之前,别的进程不能去取这些数据。

 

 

 

 

 

 

你可能感兴趣的:(Linux)