一、什么是共享内存

顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

 特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问


二、共享内存的使得

与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h中。

 1、shmget函数

该函数用来创建共享内存,它的原型为:

1. 

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

2. 

3. 

第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

 第二个参数size以字节为单位指定需要共享的内存容量

 第三个参数shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。嵌入式物联网系统学习企鹅意义气呜呜吧久零就易,共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

 2、shmat函数

第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:

1. 

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

2. 

第一个参数,shm_id是由shmget函数返回的共享内存标识。

第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。

第三个参数,shm_flg是一组标志位,通常为0。

 调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

 3、shmdt函数

该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:

[cpp] view plain copy

1. 

int shmdt(const void *shmaddr);  

2. 

参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

 4、shmctl函数

与信号量的semctl函数一样,用来控制共享内存,它的原型如下:

 

1. 

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

2. 

第一个参数,shm_id是shmget函数返回的共享内存标识符。

 第二个参数command是要采取的操作,它可以取下面的三个值 :

    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

    IPC_RMID:删除共享内存段

 第三个参数buf是一个结构指针,它指向共享内存模式和访问权限的结构。

shmid_ds结构至少包括以下成员:

 

1. 

struct shmid_ds  

2. 

3. 

{  

4. 

5. 

    uid_t shm_perm.uid;  

6. 

7. 

    uid_t shm_perm.gid;  

8. 

9. 

    mode_t shm_perm.mode;  

10. 

11. 

};  

12. 

三、使用共享内存进行进程间通信

说了这么多,又到了实战的时候了。下面就以两个不相关的进程来说明进程间如何通过共享内存来进行通信。其中一个文件shmread.c创建共享内存,并读取其中的信息,另一个文件shmwrite.c向共享内存中写入数据。为了方便操作和数据结构的统一,为这两个文件定义了相同的数据结构,定义在文件shmdata.c中。结构shared_use_st中的written作为一个可读或可写的标志,非0:表示可读,0表示可写,text则是内存中的文件。

 

shmdata.h的源代码如下:

 

1. 

#ifndef _SHMDATA_H_HEADER  

2. 

3. 

#define _SHMDATA_H_HEADER  

4. 

5. 

  

6. 

7. 

#define TEXT_SZ 2048  

8. 

9. 

  

10. 

11. 

struct shared_use_st  

12. 

13. 

{  

14. 

15. 

    int written;//作为一个标志,非0:表示可读,0表示可写  

16. 

17. 

    char text[TEXT_SZ];//记录写入和读取的文本  

18. 

19. 

};  

20. 

21. 

  

22. 

23. 

#endif  

24. 

源文件shmread.c的源代码如下:

 

1. 

#include <unistd.h>  

2. 

3. 

#include <stdlib.h>  

4. 

5. 

#include <stdio.h>  

6. 

7. 

#include <sys/shm.h>  

8. 

9. 

#include "shmdata.h"  

10. 

11. 

  

12. 

13. 

int main()  

14. 

15. 

{  

16. 

17. 

    int running = 1;//程序是否继续运行的标志  

18. 

19. 

    void *shm = NULL;//分配的共享内存的原始首地址  

20. 

21. 

    struct shared_use_st *shared;//指向shm  

22. 

23. 

    int shmid;//共享内存标识符  

24. 

25. 

    //创建共享内存  

26. 

27. 

    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  

28. 

29. 

    if(shmid == -1)  

30. 

31. 

    {  

32. 

33. 

        fprintf(stderr, "shmget failed\n");  

34. 

35. 

        exit(EXIT_FAILURE);  

36. 

37. 

    }  

38. 

39. 

    //将共享内存连接到当前进程的地址空间  

40. 

41. 

    shm = shmat(shmid, 0, 0);  

42. 

43. 

    if(shm == (void*)-1)  

44. 

45. 

    {  

46. 

47. 

        fprintf(stderr, "shmat failed\n");  

48. 

49. 

        exit(EXIT_FAILURE);  

50. 

51. 

    }  

52. 

53. 

    printf("\nMemory attached at %X\n", (int)shm);  

54. 

55. 

    //设置共享内存  

56. 

57. 

    shared = (struct shared_use_st*)shm;  

58. 

59. 

    shared->written = 0;  

60. 

61. 

    while(running)//读取共享内存中的数据  

62. 

63. 

    {  

64. 

65. 

        //没有进程向共享内存定数据有数据可读取  

66. 

67. 

        if(shared->written != 0)  

68. 

69. 

        {  

70. 

71. 

            printf("You wrote: %s", shared->text);  

72. 

73. 

            sleep(rand() % 3);  

74. 

75. 

            //读取完数据,设置written使共享内存段可写  

76. 

77. 

            shared->written = 0;  

78. 

79. 

            //输入了end,退出循环(程序)  

80. 

81. 

            if(strncmp(shared->text, "end", 3) == 0)  

82. 

83. 

                running = 0;  

84. 

85. 

        }  

86. 

87. 

        else//有其他进程在写数据,不能读取数据  

88. 

89. 

            sleep(1);  

90. 

91. 

    }  

92. 

93. 

    //把共享内存从当前进程中分离  

94. 

95. 

    if(shmdt(shm) == -1)  

96. 

97. 

    {  

98. 

99. 

        fprintf(stderr, "shmdt failed\n");  

100. 

101. 

        exit(EXIT_FAILURE);  

102. 

103. 

    }  

104. 

105. 

    //删除共享内存  

106. 

107. 

    if(shmctl(shmid, IPC_RMID, 0) == -1)  

108. 

109. 

    {  

110. 

111. 

        fprintf(stderr, "shmctl(IPC_RMID) failed\n");  

112. 

113. 

        exit(EXIT_FAILURE);  

114. 

115. 

    }  

116. 

117. 

    exit(EXIT_SUCCESS);  

118. 

119. 

}  

120. 

源文件shmwrite.c的源代码如下:

 

1. 

#include <unistd.h>  

2. 

3. 

#include <stdlib.h>  

4. 

5. 

#include <stdio.h>  

6. 

7. 

#include <string.h>  

8. 

9. 

#include <sys/shm.h>  

10. 

11. 

#include "shmdata.h"  

12. 

13. 

  

14. 

15. 

int main()  

16. 

17. 

{  

18. 

19. 

    int running = 1;  

20. 

21. 

    void *shm = NULL;  

22. 

23. 

    struct shared_use_st *shared = NULL;  

24. 

25. 

    char buffer[BUFSIZ + 1];//用于保存输入的文本  

26. 

27. 

    int shmid;  

28. 

29. 

    //创建共享内存  

30. 

31. 

    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  

32. 

33. 

    if(shmid == -1)  

34. 

35. 

    {  

36. 

37. 

        fprintf(stderr, "shmget failed\n");  

38. 

39. 

        exit(EXIT_FAILURE);  

40. 

41. 

    }  

42. 

43. 

    //将共享内存连接到当前进程的地址空间  

44. 

45. 

    shm = shmat(shmid, (void*)0, 0);  

46. 

47. 

    if(shm == (void*)-1)  

48. 

49. 

    {  

50. 

51. 

        fprintf(stderr, "shmat failed\n");  

52. 

53. 

        exit(EXIT_FAILURE);  

54. 

55. 

    }  

56. 

57. 

    printf("Memory attached at %X\n", (int)shm);  

58. 

59. 

    //设置共享内存  

60. 

61. 

    shared = (struct shared_use_st*)shm;  

62. 

63. 

    while(running)//向共享内存中写数据  

64. 

65. 

    {  

66. 

67. 

        //数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本  

68. 

69. 

        while(shared->written == 1)  

70. 

71. 

        {  

72. 

73. 

            sleep(1);  

74. 

75. 

            printf("Waiting...\n");  

76. 

77. 

        }  

78. 

79. 

        //向共享内存中写入数据  

80. 

81. 

        printf("Enter some text: ");  

82. 

83. 

        fgets(buffer, BUFSIZ, stdin);  

84. 

85. 

        strncpy(shared->text, buffer, TEXT_SZ);  

86. 

87. 

        //写完数据,设置written使共享内存段可读  

88. 

89. 

        shared->written = 1;  

90. 

91. 

        //输入了end,退出循环(程序)  

92. 

93. 

        if(strncmp(buffer, "end", 3) == 0)  

94. 

95. 

            running = 0;  

96. 

97. 

    }  

98. 

99. 

    //把共享内存从当前进程中分离  

100. 

101. 

    if(shmdt(shm) == -1)  

102. 

103. 

    {  

104. 

105. 

        fprintf(stderr, "shmdt failed\n");  

106. 

107. 

        exit(EXIT_FAILURE);  

108. 

109. 

    }  

110. 

111. 

    sleep(2);  

112. 

113. 

    exit(EXIT_SUCCESS);  

114. 

115. 

}  

116. 

再来看看运行的结果: