0


【在Linux世界中追寻伟大的One Piece】System V共享内存

1 -> System V共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

示意图:

1.1 -> 共享内存数据结构

struct shmid_ds {
    struct ipc_perm shm_perm; /* operation perms */
    int shm_segsz; /* size of segment (bytes) */
    __kernel_time_t shm_atime; /* last attach time */
    __kernel_time_t shm_dtime; /* last detach time */
    __kernel_time_t shm_ctime; /* last change time */
    __kernel_ipc_pid_t shm_cpid; /* pid of creator */
    __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
    unsigned short shm_nattch; /* no. of current attaches */
    unsigned short shm_unused; /* compatibility */
    void* shm_unused2; /* ditto - used by DIPC */
    void* shm_unused3; /* unused */
};

1.2 -> 共享内存函数

1.2.1 -> shmget函数

**功能:用来创建共享内存 **

**原型 **

**int shmget(key_t key, size_t size, int shmflg); **

**参数 **

**key:这个共享内存段名字 **

**size:共享内存大小 **

**shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的 **

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

1.2.2 -> shmot函数

**功能:将共享内存段连接到进程地址空间 **

**原型 **

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

**参数 **

**shmid: 共享内存标识 **

**shmaddr:指定连接的地址 **

**shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY **

**返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1 **

说明:

**shmaddr为NULL,核心自动选择一个地址 **

**shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。 **

**shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - **

**(shmaddr % SHMLBA) **

**shmflg=SHM_RDONLY,表示连接操作用来只读共享内存 **

1.2.3 -> shmdt函数

**功能:将共享内存段与当前进程脱离 **

**原型 **

**int shmdt(const void *shmaddr); **

**参数 **

**shmaddr: 由shmat所返回的指针 **

**返回值:成功返回0;失败返回-1 **

注意:将共享内存段与当前进程脱离不等于删除共享内存段

1.2.4 -> shmctl函数

**功能:用于控制共享内存 **

**原型 **

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

**参数 **

**shmid:由shmget返回的共享内存标识码 **

**cmd:将要采取的动作(有三个可取值) **

**buf:指向一个保存着共享内存的模式状态和访问权限的数据结构 **

**返回值:成功返回0;失败返回-1 **

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

1.3 -> 实例代码

测试代码结构

**# ls **

**client.c comm.c comm.h Makefile server.c **

**# cat Makefile **

**.PHONY:all **

**all:server client **

**client:client.c comm.c **

**gcc -o $@ $^ **

**server:server.c comm.c **

**gcc -o $@ $^ **

**.PHONY:clean **

**clean: **

**rm -f client server **

comm.h

#ifndef COMM_H
#define COMM_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);
#endif

comm.c

#include "comm.h"

static int commShm(int size, int flags)
{
    key_t _key = ftok(PATHNAME, PROJ_ID);
    if (_key < 0) 
    {
        perror("ftok");

        return -1;
    }

    int shmid = 0;
    if ((shmid = shmget(_key, size, flags)) < 0) 
    {
        perror("shmget");

        return -2;
    }

    return shmid;
}

int destroyShm(int shmid)
{
    if (shmctl(shmid, IPC_RMID, NULL) < 0) 
    {
        perror("shmctl");

        return -1;
    }

    return 0;
}

int createShm(int size)
{
    return commShm(size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(int size)
{
    return commShm(size, IPC_CREAT);
}

server.c

#include "comm.h"

int main()
{
    int shmid = createShm(4096);
    char* addr = shmat(shmid, NULL, 0);

    sleep(2);
    int i = 0;
    while (i++ < 26) 
    {
        printf("client# %s\n", addr);

        sleep(1);
    }

    shmdt(addr);

    sleep(2);

    destroyShm(shmid);

    return 0;
}

client.c

#include "comm.h"

int main()
{
    int shmid = getShm(4096);
    sleep(1);

    char* addr = shmat(shmid, NULL, 0);
    sleep(2);

    int i = 0;
    while (i < 26) 
    {
        addr[i] = 'A' + i;
        i++;
        addr[i] = 0;
        sleep(1);
    }

    shmdt(addr);

    sleep(2);

    return 0;
}

ctrl+c终止进程,再次重启。

2 -> System V消息队列

System V消息队列是一种进程间通信(IPC)机制,它允许进程通过消息的形式进行数据交换。消息队列由内核管理,可以存储多种类型的消息,并且支持消息的有序存取。每个消息都有一个类型字段,接收进程可以根据消息类型来接收特定的消息。

消息队列的关键数据结构

消息队列的状态和配置信息存储在struct msqid_ds数据结构中,它包含了队列的权限、消息计数、最大消息大小、队列字节数、最近操作进程的PID等信息。

消息队列的创建与操作

  • 创建或打开消息队列使用msgget函数,该函数接受一个键值(key)和标志(msgflg)作为参数。如果消息队列不存在且msgflg包含IPC_CREAT标志,则会创建一个新的消息队列。
  • 向消息队列发送消息使用msgsnd函数,接收消息使用msgrcv函数。这些函数允许进程指定消息的类型和大小,以及接收消息时的行为(例如阻塞或非阻塞)。
  • 控制消息队列的状态,如删除消息队列或获取消息队列的统计信息,使用msgctl函数。

消息队列的编程示例

在编程实践中,可以通过创建发送进程和接收进程来演示消息队列的使用。发送进程将数据封装成消息并发送到队列,接收进程则从队列中取出消息进行处理。这种模式适用于生产者-消费者场景,其中一个或多个进程产生数据(生产者),另一个或多个进程消费数据(消费者)。

消息队列的实际应用

消息队列不仅限于简单的数据传递,它们还可以用于更复杂的通信模式,如实现信号量或实现更高级的同步机制。在多进程或多线程的应用程序中,消息队列提供了一种灵活且高效的通信手段。

3 -> System V信号量

System V信号量是一种进程间同步机制,它允许多个进程通过对共享资源的访问计数来进行协调。信号量可以是二元的(用于互斥),也可以是非负整数(用于资源计数)。System V信号量由内核管理,并通过一系列系统调用来创建、操作和销毁。

System V信号量的关键数据结构

System V信号量的核心数据结构是semid_ds,它包含了信号量集的权限、信号量的值、信号量的状态信息等。每个信号量集中的信号量由sem结构表示,其中包含信号量的当前值和相关的进程计数信息。

System V信号量的创建与操作

创建信号量集使用semget函数,该函数接受一个键值(key)、信号量的数量(nsems)和标志(semflg)作为参数。操作信号量集使用semop函数,该函数接受信号量集的标识符、指向sembuf结构数组的指针以及操作的数量作为参数。sembuf结构定义了对信号量执行的具体操作,如等待(P)或信号(V)操作。

System V信号量的编程示例

在编程中,可以通过定义信号量集、初始化信号量值、执行P和V操作以及最终销毁信号量集来实现进程间同步。例如,一个生产者-消费者问题可以通过信号量来确保生产者不会超过消费者的消费速度,防止缓冲区溢出。

System V信号量的实际应用

System V信号量广泛应用于操作系统中,用于实现进程间的同步和互斥。它们可以用于控制对共享资源的访问,管理进程的执行顺序,以及实现更复杂的同步算法。


感谢各位大佬支持!!!

互三啦!!!

标签: linux 运维 服务器

本文转载自: https://blog.csdn.net/weixin_74809706/article/details/142683452
版权归原作者 枫叶丹4 所有, 如有侵权,请联系我们删除。

“【在Linux世界中追寻伟大的One Piece】System V共享内存”的评论:

还没有评论