Linux inter-process communication - Shared Memory

Linux inter-process communication - Shared Memory

A shared memory Introduction

     Shared memory can be understood literally, put out a logical shared memory, so that different processes to access it, modify it. For sharing memory is shared and passed between the two processes are running the data is very efficient manner. Shared between different processes of memory is usually scheduled for the same period of physical memory. A process can be connected to the same period of shared memory address space of their own, all processes can access shared memory address, as if they were a memory allocation in C language functions malloc same. And if a process writes data to the shared memory, the changes made will immediately affect any other process can access the same period of shared memory.

But one thing particular attention: shared memory does not provide a synchronization mechanism. That is, before the end of the first process writes to the shared memory, there is no automatic mechanism prevents a second process began reading it. So we often need to use other mechanisms to synchronize access to shared memory, such as semaphores.

Two shared memory usage

 ◆ create a shared memory

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

◇ a first parameter associated with a return key shared memory identifier (nonnegative integer) named shared memory segment, shmget function succeeds, the shared memory for a subsequent function. Call failed to return -1.

    ☆ other processes can return value of the function to access the same shared memory, a resource, a program which represents the process may be used to access all shared memory are indirect, the first program by calling shmget function and provide a key, then generate a corresponding shared memory identifier (shmget function return value), direct use only shmget semaphore function keys, a function of the amount of all other signals used by the semaphore identifier returned by the function semget system.

◇ The second parameter, size in bytes specified shared memory capacity required.

◇ The third parameter, shmflg permissions sign, its role and function in the same mode parameters open, if it is to identify the key in the shared memory does not exist, create it, you can do or operation IPC_CREAT. Shared memory read and write permissions to the file permission flags, like, for example, 0644, which represents a process allows the creation of shared memory owned by the creator of process memory to the shared memory read and write data, while others created the process can only read the shared memory.

 ◆ Start the shared memory access

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

When finished ◇ first create a shared memory, it can not be accessed by any process, the role of shmat function is used to start access to the shared memory and shared memory connected to the address space of the current process.

◇ The first parameter, shm_id shmget is returned by the function of identifying the shared memory.

◇ The second parameter, shm_addr specify shared memory connected to the address location of the current process, usually empty, to tell the system to select the shared memory address.

◇ The third parameter, shm_flg is a group of bits, typically 0.

Returns a pointer to the first byte of the shared memory pointer ◇ call is successful, returns -1 if the call fails.

 ◆ shared memory separate from the current process

int shmdt(const void *shmaddr);

◇ This function is used shared memory separate from the current process. Note that shared memory is not isolated to remove it, just that the current process of shared memory is no longer available.

◇ parameter is shmaddr shmat address pointer returned by the function, returns 0 when the call is successful, -1 on failure.

 ◆ shared memory control

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

◇ The first parameter, shm_id shmget function returns a shared memory identifier.

◇ The second parameter, command operation is to be taken, it may take the following three values:

    IPC_STAT: shmid_ds the data structure associated with the current set value of the shared memory, i.e. shmid_ds coverage value with the current value of the associated shared memory.

    IPC_SET: If the process has sufficient authority to put the shared memory associated with the current value is set to a value structure given shmid_ds

    IPC_RMID: Delete the shared memory segment

◇ The third parameter, buf structure is a pointer to the shared memory access patterns and structures.

 

  1. struct shmid_ds
  2. {
  3. uid_t shm_perm.uid;
  4. uid_t shm_perm.gid;
  5. mode_t shm_perm.mode;

 

Three examples

shmdata.h source:

 

  1. #ifndef _SHMDATA_H_HEADER
  2. #define _SHMDATA_H_HEADER
  3. #define TEXT_SZ 2048
  4. struct shared_use_st
  5. {
  6. int written;/* 作为一个标志,非0:表示可读,0表示可写 */
  7. char text[TEXT_SZ];/* 记录写入和读取的文本 */
  8. };
  9. #endif

 

shmread.c source

 

  1. #include<unistd.h>
  2. #include<stdlib.h>
  3. #include<stdio.h>
  4. #include<sys/shm.h>
  5. #include"shmdata.h"
  1.  
  2. #define MEM_KEY (1234)
  3.  
  4. int main()
  5. {
  6. int running =1; //程序是否继续运行的标志
  7. void*shm = NULL; //分配的共享内存的原始首地址
  8. struct shared_use_st *shared;//指向shm
  9. int shmid; //共享内存标识符
  10. //创建共享内存
  11.  
  12. shmid = shmget((key_t)MEM_KEY,sizeof(struct shared_use_st),0666|IPC_CREAT);
  13. if(shmid ==-1)
  14. {
  15. fprintf(stderr,"shmget failed\n");
  16. exit(EXIT_FAILURE);
  17. }
  18. //将共享内存连接到当前进程的地址空间
  19. shm = shmat(shmid,0,0);
  20. if(shm ==(void*)-1)
  21. {
  22. fprintf(stderr,"shmat failed\n");
  23. exit(EXIT_FAILURE);
  24. }
  25. printf("\nMemory attached at %X\n",(int)shm);
  26. //设置共享内存
  27. shared =(struct shared_use_st*)shm;
  28. shared->written =0;
  29. while(running)//读取共享内存中的数据
  30. {
  31. //没有进程向共享内存定数据有数据可读取
  32. if(shared->written !=0)
  33. {
  34. printf("You wrote: %s", shared->text);
  35. sleep(rand()%3);
  36. //读取完数据,设置written使共享内存段可写
  37. shared->written =0;
  38. //输入了end,退出循环(程序)
  39. if(strncmp(shared->text,"end",3)==0)
  40. running =0;
  41. }
  42. else//有其他进程在写数据,不能读取数据
  43. sleep(1);
  44. }
  45. //把共享内存从当前进程中分离
  46. if(shmdt(shm)==-1)
  47. {
  48. fprintf(stderr,"shmdt failed\n");
  49. exit(EXIT_FAILURE);
  50. }
  51. //删除共享内存
  52. if(shmctl(shmid, IPC_RMID,0)==-1)
  53. {
  54. fprintf(stderr,"shmctl(IPC_RMID) failed\n");
  55. exit(EXIT_FAILURE);
  56. }
  57. exit(EXIT_SUCCESS);
  58. }

 

shmwrite.c source

 

  1. #include<unistd.h>
  2. #include<stdlib.h>
  3. #include<stdio.h>
  4. #include<string.h>
  5. #include<sys/shm.h>
  6. #include"shmdata.h"
  7.  
  8. #define MEM_KEY (1234)
  9.  
  10. int main()
  11. {
  12. int running =1;
  13. void*shm = NULL;
  14. struct shared_use_st *shared = NULL;
  15. char buffer[BUFSIZ +1];//用于保存输入的文本
  16. int shmid;
  17. //创建共享内存
  18. shmid = shmget((key_t)MEM_KEY,sizeof(struct shared_use_st),0666|IPC_CREAT);
  19. if(shmid ==-1)
  20. {
  21. fprintf(stderr,"shmget failed\n");
  22. exit(EXIT_FAILURE);
  23. }
  24. //将共享内存连接到当前进程的地址空间
  25. shm = shmat(shmid,(void*)0,0);
  26. if(shm ==(void*)-1)
  27. {
  28. fprintf(stderr,"shmat failed\n");
  29. exit(EXIT_FAILURE);
  30. }
  31. printf("Memory attached at %X\n",(int)shm);
  32. //设置共享内存
  33. shared =(struct shared_use_st*)shm;
  34. while(running)//向共享内存中写数据
  35. {
  36. //数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
  37. while(shared->written ==1)
  38. {
  39. sleep(1);
  40. printf("Waiting...\n");
  41. }
  42. //向共享内存中写入数据
  43. printf("Enter some text: ");
  44. fgets(buffer, BUFSIZ, stdin);
  45. strncpy(shared->text, buffer, TEXT_SZ);
  46. //写完数据,设置written使共享内存段可读
  47. shared->written =1;
  48. //输入了end,退出循环(程序)
  49. if(strncmp(buffer,"end",3)==0)
  50. running =0;
  51. }
  52. //把共享内存从当前进程中分离
  53. if(shmdt(shm)==-1)
  54. {
  55. fprintf(stderr,"shmdt failed\n");
  56. exit(EXIT_FAILURE);
  57. }
  58. sleep(2);
  59. exit(EXIT_SUCCESS);
  60. }

 

◆ code analysis:

◇ program shmread create a shared memory, and then connect it to their own address space. Use a structure struct_use_st at the beginning of shared memory. The structure has a flag written, when there are other processes in the shared memory to write data to it, written in the shared memory is set to 0, the program waits. When it is not 0, the process indicates no shared memory to write data, the program reads data from the shared memory and output and then reset the settings written in the shared memory to 0, i.e. allowed to be written to process shmwrite data.

◇ program shmwrite get shared memory and connect to its own address space. Check the shared memory written, it is 0, if not, represent the data in shared memory has not been finished, then wait for the other to complete the process of reading and prompts the user to wait. If the shared memory is written to zero, indicating that no other processes to share memory read, the user is prompted to enter text, and set up again shared memory written to 1, indicating the completion of writing, other processes can be read on the shared memory.

◆ security discussion about the previous example

This program is unsafe, when there are multiple programs simultaneously read and write data to the shared memory, problems will arise. You might think, can be written to change the use, for example, only when written to 0 process can write data to the shared memory, and when a process is not only written to be read 0:00, the same time the written are incremented, decremented after the read operation. It's a bit like a read-write lock file lock function. First glance, it seems to work. But this is not an atomic operation, so this practice is not in line. Imagine when written is 0, if there are two processes simultaneously access the shared memory, they will find written as a 0, so both processes which are written, apparently not. When 1 is written, there are two processes simultaneously shared memory such as a read operation is more, when the two processes are complete is read, written becomes -1.

To get the program carried out safely, there should be a process whereby binary synchronous, into the operation to ensure that the critical region is an atomic operation. For example, the amount of signal earlier spoken to the synchronization process. Since all atomic semaphore operation.

3 advantages and disadvantages of using shared memory

◇ advantage: we can see the use of shared memory communication really is very convenient between processes, but also simple interface functions, data sharing also allows data transfer between processes do not, but direct access to memory, also accelerated program s efficiency. It also is not as requested anonymity pipeline process communication have some father-son relationship.

◇ Disadvantages: shared memory does not provide a mechanism to synchronize, which makes us in the use of shared memory for inter-process communication, often to synchronize work between processes by means of other means.

Four better example

1、server.c

 

  1. /*server.c:向共享内存中写入People*/
  2. #include<stdio.h>
  3. #include<sys/types.h>
  4. #include<sys/ipc.h>
  5. #include<sys/sem.h>
  6. #include<string.h>
  7.  
  8. #include"credis.h"
  9.  
  10. int semid;
  11. int shmid;
  12.  
  13. /*信号量的P操作*/
  14. void p()
  15. {
  16. struct sembuf sem_p;
  17. sem_p.sem_num=0;/*设置哪个信号量*/
  18. sem_p.sem_op=-1;/*定义操作*/
  19. if(semop(semid,&sem_p,1)==-1)
  20. printf("p operation is fail\n");
  21. /*semop函数自动执行信号量集合上的操作数组。
  22.    int semop(int semid, struct sembuf semoparray[], size_t nops);
  23.    semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。*/
  24. }
  25.  
  26. /*信号量的V操作*/
  27. void v()
  28. {
  29. struct sembuf sem_v;
  30. sem_v.sem_num=0;
  31. sem_v.sem_op=1;
  32. if(semop(semid,&sem_v,1)==-1)
  33. printf("v operation is fail\n");
  34. }
  35.  
  36. int main()
  37. {
  38. structPeople{
  39. char name[10];
  40. int age;
  41. };
  42.  
  43. key_t semkey;
  44. key_t shmkey;
  45. semkey=ftok("../test/VenusDB.cbp",0);//用来产生唯一的标志符,便于区分信号量及共享内存
  46. shmkey=ftok("../test/main.c",0);
  47.  
  48. /*创建信号量的XSI IPC*/
  49. semid=semget(semkey,1,0666|IPC_CREAT);//参数nsems,此时为中间值1,指定信号灯集包含信号灯的数目
  50. //0666|IPC_CREAT用来表示对信号灯的读写权限
  51. /*
  52. 从左向右:
  53. 第一位:0表示这是一个8进制数
  54. 第二位:当前用户的经权限:6=110(二进制),每一位分别对就 可读,可写,可执行,6说明当前用户可读可写不可执行
  55. 第三位:group组用户,6的意义同上
  56. 第四位:其它用户,每一位的意义同上,0表示不可读不可写也不可执行
  57. */
  58. if(semid==-1)
  59. printf("creat sem is fail\n");
  60. //创建共享内存
  61. shmid=shmget(shmkey,1024,0666|IPC_CREAT);//对共享内存
  62. if(shmid==-1)
  63. printf("creat shm is fail\n");
  64.  
  65. /*设置信号量的初始值,就是资源个数*/
  66. union semun{
  67. int val;
  68. struct semid_ds *buf;
  69. unsignedshort*array;
  70. }sem_u;
  71.  
  72. sem_u.val=1;/*设置变量值*/
  73. semctl(semid,0,SETVAL,sem_u);//初始化信号量,设置第0个信号量,p()操作为非阻塞的
  74.  
  75. /*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/
  76.  
  77. structPeople*addr;
  78. addr=(structPeople*)shmat(shmid,0,0);//将共享内存映射到调用此函数的内存段
  79. if(addr==(structPeople*)-1)
  80. printf("shm shmat is fail\n");
  81.  
  82. /*向共享内存写入数据*/
  83. p();
  84. strcpy((*addr).name,"xiaoming");
  85. /*注意:①此处只能给指针指向的地址直接赋值,不能在定义一个 struct People people_1;addr=&people_1;因为addr在addr=(struct People*)shmat(shmid,0,0);时,已经由系统自动分配了一个地址,这个地址与共享内存相关联,所以不能改变这个指针的指向,否则他将不指向共享内存,无法完成通信了。
  86. 注意:②给字符数组赋值的方法。刚才太虎了。。*/
  87. (*addr).age=10;
  88. v();
  89.  
  90. /*将共享内存与当前进程断开*/
  91. if(shmdt(addr)==-1)
  92. printf("shmdt is fail\n");
  93. }

2、clinet.c

  1. /*client.c:从共享内存中读出People*/
  2. #include<stdio.h>
  3. #include<sys/types.h>
  4. #include<sys/ipc.h>
  5. #include<sys/sem.h>
  6.  
  7. int semid;
  8. int shmid;
  9.  
  10. /*信号量的P操作*/
  11. void p()
  12. {
  13. struct sembuf sem_p;
  14. sem_p.sem_num=0;
  15. sem_p.sem_op=-1;
  16. if(semop(semid,&sem_p,1)==-1)
  17. printf("p operation is fail\n");
  18. }
  19.  
  20. /*信号量的V操作*/
  21. void v()
  22. {
  23. struct sembuf sem_v;
  24. sem_v.sem_num=0;
  25. sem_v.sem_op=1;
  26. if(semop(semid,&sem_v,1)==-1)
  27. printf("v operation is fail\n");
  28. }
  29.  
  30. int main()
  31. {
  32. key_t semkey;
  33. key_t shmkey;
  34. semkey=ftok("../test/client/VenusDB.cbp",0);
  35. shmkey=ftok("../test/client/main.c",0);
  36.  
  37. structPeople{
  38. char name[10];
  39. int age;
  40. };
  41.  
  42. /*读取共享内存和信号量的IPC*/
  43. semid=semget(semkey,0,0666);
  44. if(semid==-1)
  45. printf("creat sem is fail\n");
  46. shmid=shmget(shmkey,0,0666);
  47. if(shmid==-1)
  48. printf("creat shm is fail\n");
  49.  
  50. /*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/
  51. structPeople*addr;
  52. addr=(structPeople*)shmat(shmid,0,0);
  53. if(addr==(structPeople*)-1)
  54. printf("shm shmat is fail\n");
  55.  
  56. /*从共享内存读出数据*/
  57. p();
  58. printf("name:%s\n",addr->name);
  59. printf("age:%d\n",addr->age);
  60. v();
  61.  
  62. /*将共享内存与当前进程断开*/
  63. if(shmdt(addr)==-1)
  64. printf("shmdt is fail\n");
  65.  
  66. /*IPC必须显示删除。否则会一直留存在系统中*/
  67. if(semctl(semid,0,IPC_RMID,0)==-1)
  68. printf("semctl delete error\n");
  69. if(shmctl(shmid,IPC_RMID,NULL)==-1)
  70. printf("shmctl delete error\n");
  71. }

Five father and son shared memory sub-process

  1. #include<stdio.h>
  2. #include<sys/types.h>
  3. #include<sys/ipc.h>
  4. #include<sys/sem.h>
  5.  
  6. #define SHM_KEY 0x33
  7. #define SEM_KEY 0x44
  8.  
  9. union semun {
  10. int val;
  11. struct semid_ds *buf;
  12. unsignedshort*array;
  13. };
  14.  
  15. int P(int semid)
  16. {
  17. struct sembuf sb;
  18. sb.sem_num =0;
  19. sb.sem_op =-1;
  20. sb.sem_flg = SEM_UNDO;
  21.  
  22. if(semop(semid,&sb,1)==-1){
  23. perror("semop");
  24. return-1;
  25. }
  26. return0;
  27. }
  28.  
  29. int V(int semid)
  30. {
  31. struct sembuf sb;
  32. sb.sem_num =0;
  33. sb.sem_op =1;
  34. sb.sem_flg = SEM_UNDO;
  35.  
  36. if(semop(semid,&sb,1)==-1){
  37. perror("semop");
  38. return-1;
  39. }
  40. return0;
  41. }
  42.  
  43. int main(int argc,char**argv)
  44. {
  45. pid_t pid;
  46. int i, shmid, semid;
  47. int*ptr;
  48. union semun semopts;
  49.  
  50. /* 创建一块共享内存, 存一个int变量 */
  51. if((shmid = shmget(SHM_KEY,sizeof(int), IPC_CREAT |0600))==-1){
  52. perror("msgget");
  53. }
  54.  
  55. /* 将共享内存映射到进程, fork后子进程可以继承映射 */
  56. if((ptr =(int*)shmat(shmid, NULL,0))==(void*)-1){
  57. perror("shmat");
  58. }
  59. *ptr =0;
  60.  
  61. /* 创建一个信号量用来同步共享内存的操作 */
  62. if((semid = semget(SEM_KEY,1, IPC_CREAT |0600))==-1){
  63. perror("semget");
  64. }
  65.  
  66. /* 初始化信号量 */
  67. semopts.val =1;
  68. if(semctl(semid,0, SETVAL, semopts)<0){
  69. perror("semctl");
  70. }
  71.  
  72. if((pid = fork())<0){
  73. perror("fork");
  74. }elseif(pid ==0){/* Child */
  75. /* 子进程对共享内存加1 */
  76. for(i =0; i <100; i++){
  77. P(semid);
  78. (*ptr)++;
  79. V(semid);
  80. printf("child: %d\n",*ptr);
  81. }
  82. }else{/* Parent */
  83. /* 父进程对共享内存减1 */
  84. for(i =0; i <100; i++){
  85. P(semid);
  86. (*ptr)--;
  87. V(semid);
  88. printf("parent: %d\n",*ptr);
  89. }
  90. waitpid(pid);
  91. sleep(2);
  92. /* 如果同步成功, 共享内存的值为0 */
  93. printf("finally: %d\n",*ptr);
  94. }
  95.  
  96. return0;
  97. }
Published 101 original articles · won praise 73 · views 120 000 +

Guess you like

Origin blog.csdn.net/usstmiracle/article/details/104374743