Unix IPC之基于共享内存的计数器

目的

本文主要实现一个基于共享内存的计数器,通过父子进程对其访问。

本文程序需基于<<Unix网络编程-卷2>>的环境才能运行。程序中大写开头的函数为其小写同名函数的包裹函数,增加了错误处理信息。

1 函数介绍

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

#include <sys/mman>

/**

 * Map addresses starting near ADDR and extending for LEN bytes.

 * from OFFSET into the file FD describes according to PROT and FLAGS.

 * If ADDR is nonzero, it is the desired mapping address.

 * If the MAP_FIXED bit is set in FLAGS, the mapping will be at ADDR exactly (which must be

 * page-aligned); otherwise the system chooses a convenient nearby address.

 * The return value is the actual mapping address chosen or MAP_FAILED

 * for errors (in which case `errno‘ is set).  A successful `mmap‘ call

 * deallocates any previous mapping for the affected region.

 */

void *mmap (void *__addr, size_t __len, int __prot,

            int __flags, int __fd, __off_t __offset);

/* Deallocate any mapping for the region starting at ADDR and extending LEN

   bytes.  Returns 0 if successful, -1 for errors (and sets errno).  */

extern int munmap (void *__addr, size_t __len) __THROW;

/* Synchronize the region starting at ADDR and extending LEN bytes with the

   file it maps.  Filesystem operations on a file being mapped are

   unpredictable before this is done.  Flags are from the MS_* set.

   This function is a cancellation point and therefore not marked with

   __THROW.  */

extern int msync (void *__addr, size_t __len, int __flags);

2 计数器——非共享内存

默认情况下,通过fork派生的子进程并不与其父进程共享内存区。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

#include    "unpipc.h"

#define SEM_NAME    "mysem"

int     count = 0; // 计数器

int main(int argc, char **argv)

{

    int     i, nloop;

    sem_t   *mutex;

    if (argc != 2)

        err_quit("usage: incr1 <#loops>");

    nloop = atoi(argv[1]);

    /* 4create, initialize, and unlink semaphore */

    mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1);

    /**

     * sem_unlink() 

     * removes the named semaphore referred to by name. 

     * The semaphore name is removed immediately. 

     * The semaphore is destroyed once all other processes that have the semaphore open close it.

     */

    Sem_unlink(Px_ipc_name(SEM_NAME));

    // 非缓冲模式,防止两个线程的输出交叉

    // 父子线程共同访问一个信号量

    // 由于mutex是2值信号量,相当于同步父子线程

    // 所以实际上这里并不会发生交叉

    setbuf(stdout, NULL);   /* stdout is unbuffered */

    if (Fork() == 0)        /* child */

    {

        for (i = 0; i < nloop; i++)

        {

            Sem_wait(mutex);

            printf("child: %d\n", count++);

            Sem_post(mutex);

        }

        exit(0);

    }

    /* 4parent */

    for (i = 0; i < nloop; i++)

    {

        Sem_wait(mutex);

        printf("parent: %d\n", count++);

        Sem_post(mutex);

    }

    exit(0);

}

由于子进程为父进程的拷贝,所以子进程自己有一个count的副本,所以父子进程操作自己的count。这里使用一个有名信号量来同步父子进程。

3 计数器——共享内存

程序说明:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

#include    "unpipc.h"

#define SEM_NAME    "mysem" // 有名信号量用于同步父子进程(加锁)

int main(int argc, char **argv)

{

    int     fd, i, nloop, zero = 0;

    int     *ptr; // 访问共享内存的指针

    sem_t   *mutex;

    if (argc != 3)

        err_quit("usage: incr2 <pathname> <#loops>");

    nloop = atoi(argv[2]);

    /* 4open file, initialize to 0, map into memory */

    fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);

    Write(fd, &zero, sizeof(int)); // 向文件中写入一个int型的0

    // ptr返回共享内存起始位置

    ptr = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 映射一个int型大小的共享内存

    Close(fd);

    /* 4create, initialize, and unlink semaphore */

    mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1); // 信号量

    Sem_unlink(Px_ipc_name(SEM_NAME));

    setbuf(stdout, NULL);   /* stdout is unbuffered */

    if (Fork() == 0)        /* child */

    {

        for (i = 0; i < nloop; i++)

        {

            Sem_wait(mutex);

            printf("child: %d\n", (*ptr)++);

            Sem_post(mutex);

        }

        exit(0);

    }

    /* 4parent */

    for (i = 0; i < nloop; i++)

    {

        Sem_wait(mutex);

        printf("parent: %d\n", (*ptr)++);

        Sem_post(mutex);

    }

    exit(0);

}

运行结果:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

[[email protected] shm]$ ./incr2 ./count.data 100

parent: 0

parent: 1

parent: 2

parent: 3

....

child: 197

child: 198

child: 199

[[email protected] shm]$ ll count.data

-rw-r--r--. 1 dell dell 4 8月  18 14:28 count.data

[[email protected] shm]$ file count.data

count.data: data

[[email protected] shm]$ hexdump -d count.data

0000000   00200   00000                                               

0000004

[[email protected] shm]$

注意:这里的count.data文件类型data类型,需用od、xxd、hexdump等命令才能查看。

4 计数器——共享内存(2)

上面3中的信号量为有名信号量,其具体实现由Posix决定,但是至少是内核持续性的。这里将其改为基于内存的信号量,并将其放置在共享内存中。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

#include    "unpipc.h"

struct shared

{

    sem_t mutex;      /* the mutex: a Posix memory-based semaphore */ // 匿名信号量

    int   count;      /* and the counter */

} shared;

int main(int argc, char **argv)

{

    int     fd, i, nloop;

    struct shared   *ptr;

    if (argc != 3)

        err_quit("usage: incr3 <pathname> <#loops>");

    nloop = atoi(argv[2]);

    /* 4open file, initialize to 0, map into memory */

    fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);

    Write(fd, &shared, sizeof(struct shared));

    ptr = Mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,

               MAP_SHARED, fd, 0);

    Close(fd);

    /* 4initialize semaphore that is shared between processes */

    Sem_init(&ptr->mutex, 1, 1); // 初始化匿名信号量,设置为进程共享,初始值为1

    setbuf(stdout, NULL);   /* stdout is unbuffered */

    if (Fork() == 0)        /* child */

    {

        for (i = 0; i < nloop; i++)

        {

            Sem_wait(&ptr->mutex);

            printf("child: %d\n", ptr->count++);

            Sem_post(&ptr->mutex);

        }

        exit(0);

    }

    /* 4parent */

    for (i = 0; i < nloop; i++)

    {

        Sem_wait(&ptr->mutex);

        printf("parent: %d\n", ptr->count++);

        Sem_post(&ptr->mutex);

    }

    exit(0);

}

来自为知笔记(Wiz)

时间: 2024-10-11 12:26:39

Unix IPC之基于共享内存的计数器的相关文章

一个基于共享内存的ipc通信框架

一个基于共享内存的ipc通信框架 与共享内存相关的操作主要包括共享内存的初始化, 共享内存的释放, 共享内存的锁的相关操作, 在这里操作共享内存的环境是: 1 多个进程没有亲缘关系, 也没有server/client关系, 是多个不相关进程并发操作共享内存 2 共享内存一开始不存在, 由第一个访问他的进程创建 3 当共享内存退出时, 由最后一个离开的进程释放共享内存, 并清除信号量 在这个问题之中, 主要有两个比较大的问题: 1 怎样新建并初始化共享内存 新建共享内存的数据都可以用信号量来控制,

ACE框架 基于共享内存的进程间通讯

ACE框架将基于共享内存的进程间通讯功能,如其它IO组件或IPC组件一样,设计成三个组件.流操作组件ACE_MEM_Stream,连接器组件ACE_MEM_Connector,以及接收连接组件ACE_MEM_Accpter.ACE框架为基于共享内存的进程间通讯提供了两种数据传输(分发deliver)策略.一种是使用生产者-消费者队列的一对多的多用户MT策略,另一种是使用socket流的可以使用反应器响应数据接收事件的Reactor策略.不论哪一种策略都要通过socket进行TCP连接,并进行进程

【UNIX】什么是IPC对象以及共享内存

sys V 的IPC 对象:对于内核中创建的文件对象,就是文件标识符(它引用了文件对象的全部信息)在进程中文件描述符一般使用当前最小可用值. 对于IPC标识符返回的是索引的整数值,它是全局变量的流水号,在系统中唯一分配的,若果在创建的IPC对象没有关闭,他会全局存在,只有在系统关闭的时候才关闭,这样会造成内存空间的资源被占用. 为了进程之间交换信息,内核专门留了一块内存空间,由进程映射到各自进程私有空间. [共享内存的实现]: 1)创建/打开共享内存,这里的创建/打开和文件的创建/打开差不多,只

撸代码--linux进程通信(基于共享内存)

1.实现亲缘关系进程的通信,父写子读 思路分析:1)首先我们须要创建一个共享内存. 2)父子进程的创建要用到fork函数.fork函数创建后,两个进程分别独立的执行. 3)父进程完毕写的内容.同一时候要保证子进程退出后,在删除共享内存. 4)子进程完毕读的内容. 效果展示:                 代码展示:           #include <string.h> #include <unistd.h> #include <sys/types.h> #inc

Linux IPC实践(8) --共享内存/内存映射

概述 共享内存区是最快的IPC形式.一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图). 共享内存 VS. 其他IPC形式 用管道/消息队列传递数据 用共享内存传递数据 共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快! mmap内存映射 将文件/设备空间映射到共享内存区 #incl

IPC(SystemV) 之 共享内存

很久以来我都是只闻其名,未见其形.终于在这次系统的学习linux编程中接触到了共享内存.果然很牛. 上一篇文章中我们讲的信号量,个人感觉,严格的说,信号量只是进城通信的辅助.而共享内存才真正实现了进程通信. 共享内存机制允许两个不想关的进程访问同一段物理内存,当然得是一台主机. 头文件<sys/shm.h>   和信号量的情况一样,也需要包含sys/types.h 和 sys/ipc.h .当然有可能已经包含在了sys/shm.h中了. 共享内存函数如下: int shmget(key_t k

unix/linux下的共享内存、信号量、队列信息管理

在unix/linux下,经常有因为共享内存.信号量,队列等共享信息没有干净地清楚而引起一些问题.查看共享信息的内存的命令是ipcs [-m|-s|-q]. 默认会列出共享内存.信号量,队列信息,-m列出共享内存,-s列出共享信号量,-q列出共享队列清除命令是ipcrm [-m|-s|-q] id.-m 删除共享内存,-s删除共享信号量,-q删除共享队列.

UNIX网络编程:共享内存区

IPC形式除了管道.FIFO.信号量以外,还有共享内存区和消息队列.这里主要堆共享内存进行介绍. 共享内存区是可用IPC形式中最快的.一旦这样的内存区映射到共享它的进程地址空间,这些进程间数据的传递就不再涉及内核.共享内存与其他进程通信方式相比较,不需要复制数据,直接读写内存,是一种效率非常高的进程通信方案.但它本身不提供同步访问机制,需要我们自己控制.在LINUX中,只要把共享内存段连接到进程的地址空间中,这个进程就可以访问共享内存中的地址了.为了实现往该共享内存区存放和取出数据的进程间的同步

IPC通信_共享内存

共享内存允许两个或多个进程共享一个给定的存储区,就是多个进程将同一块物理内存映射到自己的虚拟地址上.因为不需要在客户进程和服务进程之间拷贝,所以是最快的一种IPC. 函数1 #include <sys/shm.h> int shmget(key_t key, size_t size, int flag); 该函数转换键值获取共享内存ID, key键值的获取还是通过ftok函数. 返回值:成功返回共享内存ID, 失败返回-1 参数key:键值(key) 参数size:请求的共享内存的长度,单位字