v76.01 Hongmeng kernel source code analysis (shared memory) | the fastest way of communication between processes | 100 blog analysis of OpenHarmony source code

One hundred blog Analysis | this article is: (shared memory) | the fastest way of communication between processes

Related articles of process communication are:

concept

The word "sharing good" has been broken in recent years. Sharing bicycles, charging treasure, office and umbrella There are even sharing girlfriends. It's really how bold people are and how productive sharing is. But everything is too easy to disgust people. I was disgusted by shared memory for a time. I didn't want to touch it until now.

The principle of shared memory is simple. The purpose is to communicate between processes by mapping to the same physical memory. It is a scarce resource, which is managed by the kernel according to the resource pool. The number is limited. It is 192 by default. It is uniquely identified by the resource ID. when needed, the user process applies to the kernel for the shared memory size through system call. The manager allocates an available resource ID from the resource pool and applies for the corresponding physical page box from the physical memory.

How to use shared memory involves the most important concept mapping of memory module. If you are not clear, you can read the relevant articles in the series. Processes with sharing requirements draw a linear area in their process space and map it to the shared memory segment. How to find this shared memory segment? The operation interface is provided by the system call. In short, the shared resource ID(shmid) is created through the parameter key, and then the shmid connects / deletes / controls the shared memory. See 4 system calls Shm * * * at the end of this chapter for details.

How?

This is the diagram drawn by the author after reading the kernel shared memory module. Try to use a diagram to express the content of a module. Because Baiwen is generated in the process of commenting the source code, this kind of strange diagram will be drawn, including code and model. Let's call it the code model diagram:

The figure is interpreted in two parts: management and mapping. In order to simplify the code display, only the backbone is left, and the code for judgment and inspection is deleted.

Management part

  • Initialize shared memory. Shared memory is managed in the way of resource pool, which is the global variable g_shmSegs applied to the inner nuclear reactor space g_shmInfo.shmmni a struct shmIDSource
    #define SHM_MNI 192 / / total shared memory 192 by default
    // Shared memory module setting information
    struct shminfo {
      unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4];
    };
    STATIC struct shminfo g_shmInfo = { //Global variables that describe the extent of shared memory
      .shmmax = SHM_MAX,//The upper limit of a single shared memory is 4096 pages, i.e. 16M
      .shmmin = SHM_MIN,//The lower limit of a single page of shared memory is 4K
      .shmmni = SHM_MNI,//Total shared memory 192 by default 
      .shmseg = SHM_SEG,//The maximum number of shared memory segments that can be used by each user process 128
      .shmall = SHM_ALL,//Total number of pages of shared memory within the system, 4096 pages 
      };
    //Shared memory initialization
      UINT32 ShmInit(VOID)
      {
          // ..
          ret = LOS_MuxInit(&g_sysvShmMux, NULL);//Initialize mutex
          g_shmSegs = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, sizeof(struct shmIDSource) * g_shmInfo.shmmni);//Allocate shm segment array
          (VOID)memset_s(g_shmSegs, (sizeof(struct shmIDSource) * g_shmInfo.shmmni),
                      0, (sizeof(struct shmIDSource) * g_shmInfo.shmmni));//Array zeroing
          for (i = 0; i < g_shmInfo.shmmni; i++) {
              g_shmSegs[i].status = SHM_SEG_FREE;//The initial state of the node is idle
              g_shmSegs[i].ds.shm_perm.seq = i + 1;//struct ipc_perm shm_perm; The system saves an IPC for each IPC object_ Perm structure, which describes the permissions and owners of IPC objects
              LOS_ListInit(&g_shmSegs[i].node);//Initialize node
          }
          g_shmUsedPageCount = 0;
          return LOS_OK;
      }
    
  • As mentioned many times in the series, each functional module has at least one core structure to support the operation of the module. The process is PCB, the task is TCB, and the shared memory is shmIDSource
    struct shmIDSource {//Shared memory descriptor
          struct shmid_ds ds; //Is the data structure maintained by the kernel for each shared memory segment
          UINT32 status;	//Status SHM_SEG_FREE ...
          LOS_DL_LIST node; //Node, hang VmPage
      #ifdef LOSCFG_SHELL
          CHAR ownerName[OS_PCB_NAME_LEN];
      #endif
      };
    
    First, shmid_ds is a structure that truly describes the shared memory information. It records who created the shared memory, size, user / group, access time, etc.
    //Each shared memory segment maintains an internal structure shmid in the kernel_ ds
      struct shmid_ds {
          struct ipc_perm shm_perm;///< operation license, which contains user ID, group ID and other information of shared memory
          size_t shm_segsz;	///< size of shared memory segment, in bytes
          time_t shm_atime;	///< time when the last process accessed shared memory	
          time_t shm_dtime; 	///< time when the last process left the shared memory
          time_t shm_ctime; 	///< creation time
          pid_t shm_cpid;		///< process ID to create shared memory
          pid_t shm_lpid;		///< process ID of the last operation on shared memory
          unsigned long shm_nattch;	///< number of processes currently using the shared memory segment
          unsigned long __pad1;	//Reserved for extension
          unsigned long __pad2;
      };
    //The kernel saves an IPC for each IPC object_ Perm structure, which describes the permissions and owners of IPC objects
      struct ipc_perm {
          key_t __ipc_perm_key;	//Keyword given when calling shmget()
          uid_t uid;				//Valid user ID of the shared memory owner
          gid_t gid;				//Valid group ID of the group to which the shared memory owner belongs
          uid_t cuid;				//Valid user ID of the shared memory creator
          gid_t cgid;				//Valid group ID of the group to which the shared memory creator belongs
          mode_t mode;			//Permissions + SHM_DEST / SHM_LOCKED /SHM_HUGETLB flag bit
          int __ipc_perm_seq;		//serial number
          long __pad1;			//Reserved for extension
          long __pad2;
      };  
    
    Status indicates the status of this shared memory, because it is the way of resource pool, and only SHM_ SEG_ The status of free can be allocated, and the process pool and task pool are also managed in this way.
      #define SHM_SEG_FREE    0x2000 	// Idle unused
      #define SHM_SEG_USED    0x4000 	// Used
      #define SHM_SEG_REMOVE  0x8000 	// delete
    
    On the node bidirectional linked list, there are physical page boxes vmpages, which are the core attributes, and the data will be stored in these physical page boxes. ShmAllocSeg is a specific allocation function
    STATIC INT32 ShmAllocSeg(key_t key, size_t size, INT32 shmflg)
      {
          // ... 
          count = LOS_PhysPagesAlloc(size >> PAGE_SHIFT, &seg->node);//Allocate shared pages and hang all node s inside the function
          if (count != (size >> PAGE_SHIFT)) {//When not enough memory is allocated, the processing method is: don't give so little, give up!
              (VOID)LOS_PhysPagesFree(&seg->node);//Release the physical page box on the node
              seg->status = SHM_SEG_FREE;//The shared segment returns to the idle state
              return -ENOMEM;
          }
          ShmSetSharedFlag(seg);//Set each page of the node as a shared page
          g_shmUsedPageCount += size >> PAGE_SHIFT;
          seg->status |= SHM_SEG_USED;	//The shared segment is labeled as already in use
          seg->ds.shm_perm.mode = (UINT32)shmflg & ACCESSPERMS;
          seg->ds.shm_perm.key = key;//Save the parameter key so that the key and the shared ID are bound together
          seg->ds.shm_segsz = size;	//Size of shared segment
          seg->ds.shm_perm.cuid = LOS_GetUserID();	//Set user ID
          seg->ds.shm_perm.uid = LOS_GetUserID();		//Set user ID
          seg->ds.shm_perm.cgid = LOS_GetGroupID();	//Set group ID
          seg->ds.shm_perm.gid = LOS_GetGroupID();	//Set group ID
          seg->ds.shm_lpid = 0; //The process of the last operation
          seg->ds.shm_nattch = 0;	//Number of binding processes					
          seg->ds.shm_cpid = LOS_GetCurrProcessID();	//Get process ID
          seg->ds.shm_atime = 0;	//Visit time
          seg->ds.shm_dtime = 0;	//After the detach time shared memory is used up, it needs to be separated from the process address space; Separating shared memory does not delete it, but makes the shared memory no longer available to the current process
          seg->ds.shm_ctime = time(NULL);//Creation time
      #ifdef LOSCFG_SHELL
          (VOID)memcpy_s(seg->ownerName, OS_PCB_NAME_LEN, OsCurrProcessGet()->processName, OS_PCB_NAME_LEN);
      #endif
          return segNum;
      }
    

Mapping usage section

  • Step 1: create shared memory. To realize shared memory, you must first create a memory segment for sharing. ShmGet does this
    /*!
      * @brief ShmGet	
      *	Get a shared memory identifier or create a shared memory object
      * @param key	Create a new shared memory object. The identifier is the internal name of the IPC object. In order to enable multiple cooperative processes to converge on the same IPC object, an external naming scheme needs to be provided.
              For this purpose, each IPC object is associated with a key, which is used as the external name of the object. Whenever the IPC structure is created (through msgget, semget and shmget),
              IPC should be assigned a key, key_t is created by ftok. Of course, ftok cannot be found in this project, so I have to write so much
      * @param shmflg	IPC_CREAT IPC_EXCL
                  IPC_CREAT: 	When creating a new IPC, if the key parameter is IPC_PRIVATE or independent of the current type of IPC structure, you need to specify the IPC of the flag parameter_ Creat flag bit,
                              Is used to create a new IPC structure. (if the IPC structure already exists and IPC_CREAT is specified, IPC_CREAT does nothing and the function does not make an error)
                  IPC_EXCL: 	This parameter is generally the same as IPC_CREAT is used together to create a new IPC structure. If the created IPC structure already exists, the function returns an error,
                              Returns EEXIST (the same principle as the open function specifies the o_create and O_EXCL flags)
      * @param size	New shared memory size, in bytes
      * @return	
      *
      * @see
      */
    INT32 ShmGet(key_t key, size_t size, INT32 shmflg)
      {
          SYSV_SHM_LOCK();
          if (key == IPC_PRIVATE) {
              ret = ShmAllocSeg(key, size, shmflg);
          } else {
              ret = ShmFindSegByKey(key);//Find resource ID through key
              ret = ShmAllocSeg(key, size, shmflg);//Allocate a shared memory
          }
          SYSV_SHM_UNLOCK();
          return ret;
      }
    
  • Step 2: process linear area binding shared memory shmat() function is used to start accessing the shared memory and connect the shared memory to the address space of the current process., The first parameter of ShmAt is actually the return value when ShmGet succeeds. ShmatVmmAlloc is responsible for allocating an available linear area and mapping it with the shared memory
    /*!
      * @brief ShmAt	
      * It is used to start access to the shared memory and connect the shared memory to the address space of the current process.
      * @param shm_flg Is a set of flag bits, usually 0.
      * @param shmaddr Specifies the address location where the shared memory is connected to the current process. It is usually empty, indicating that the system is allowed to select the address of the shared memory.
      * @param shmid	Is the shared memory identifier returned by the shmget() function
      * @return	
      * If shmat is executed successfully, the kernel will make the shmid associated with the shared storage_ SHM in DS structure_ Nattch counter value plus 1
      shmid It's just an index, just like the process and thread ID S G_ Shmsegs [shmid] shmid > 192
      * @see
      */
      VOID *ShmAt(INT32 shmid, const VOID *shmaddr, INT32 shmflg)
      {
          struct shmIDSource *seg = NULL;
          LosVmMapRegion *r = NULL;
          ret = ShmatParamCheck(shmaddr, shmflg);//Parameter check
          SYSV_SHM_LOCK();
          seg = ShmFindSeg(shmid);//Find segment
          ret = ShmPermCheck(seg, acc_mode);
          seg->ds.shm_nattch++;//ds records a process binding
          r = ShmatVmmAlloc(seg, shmaddr, shmflg, prot);//Allocate a linear area in the current process space and map it to shared memory
          r->shmid = shmid;//Give the ID to the shmid of the linear region
          r->regionFlags |= VM_MAP_REGION_FLAG_SHM;//This is a shared linear region
          seg->ds.shm_atime = time(NULL);//Visit time
          seg->ds.shm_lpid = LOS_GetCurrProcessID();//Process ID
          SYSV_SHM_UNLOCK();
          return (VOID *)(UINTPTR)r->range.base;
      }
    
  • Step 3: control / use shared memory, which is the purpose. The front is foreplay
    /*!
      * @brief ShmCtl	
      * This function can perform various operations (delete, retrieve information, lock, unlock, etc.) on the shared storage specified by shmid
      * @param buf	Is a structure pointer that points to the structure of shared memory mode and access rights.
      * @param cmd	command Is the action to be taken. It can take the following three values:
          IPC_STAT: Put shmid_ The data in the DS structure is set as the current association value of the shared memory, that is, the shmid is overwritten with the current association value of the shared memory_ The value of DS.
          IPC_SET: If the process has sufficient permissions, set the current association value of shared memory to shmid_ The value given in the DS structure
          IPC_RMID: Delete shared memory segment
      * @param shmid	Is the shared memory identifier returned by the shmget() function
      * @return	
      *
      * @see
      */
      INT32 ShmCtl(INT32 shmid, INT32 cmd, struct shmid_ds *buf)
      {
          SYSV_SHM_LOCK();
          switch (cmd) {
              case IPC_STAT:
              case SHM_STAT://Segment structure
                  ret = LOS_ArchCopyToUser(buf, &seg->ds, sizeof(struct shmid_ds));//Copy the shared page data in kernel space to user space
                  if (cmd == SHM_STAT) {
                      ret = (unsigned int)((unsigned int)seg->ds.shm_perm.seq << 16) | (unsigned int)((unsigned int)shmid & 0xffff); /* 16: use the seq as the upper 16 bits */
                  }
                  break;
              case IPC_SET://Reset shared segment
                  ret = ShmPermCheck(seg, SHM_M);
                  //Copy data from user space to kernel space
                  ret = LOS_ArchCopyFromUser(&shm_perm, &buf->shm_perm, sizeof(struct ipc_perm));
                  seg->ds.shm_perm.uid = shm_perm.uid;
                  seg->ds.shm_perm.gid = shm_perm.gid;
                  seg->ds.shm_perm.mode = (seg->ds.shm_perm.mode & ~ACCESSPERMS) |
                                          (shm_perm.mode & ACCESSPERMS);//Accessible
                  seg->ds.shm_ctime = time(NULL);
      #ifdef LOSCFG_SHELL
                  (VOID)memcpy_s(seg->ownerName, OS_PCB_NAME_LEN, OS_PCB_FROM_PID(shm_perm.uid)->processName,
                              OS_PCB_NAME_LEN);
      #endif
                  break;
              case IPC_RMID://Delete shared segment
                  ret = ShmPermCheck(seg, SHM_M);
                  seg->status |= SHM_SEG_REMOVE;
                  if (seg->ds.shm_nattch <= 0) {//No process is in use
                      ShmFreeSeg(seg);//Free return memory
                  }
                  break;
              case IPC_INFO://Copy the shared page data in kernel space to user space
                  ret = LOS_ArchCopyToUser(buf, &g_shmInfo, sizeof(struct shminfo));
                  ret = g_shmInfo.shmmni;
                  break;
              case SHM_INFO:
                  shmInfo.shm_rss = 0;
                  shmInfo.shm_swp = 0;
                  shmInfo.shm_tot = 0;
                  shmInfo.swap_attempts = 0;
                  shmInfo.swap_successes = 0;
                  shmInfo.used_ids = ShmSegUsedCount();//Number of SEGS in use
                  ret = LOS_ArchCopyToUser(buf, &shmInfo, sizeof(struct shm_info));//Copy the shared page data in kernel space to user space
                  ret = g_shmInfo.shmmni;
                  break;
              default:
                  VM_ERR("the cmd(%d) is not supported!", cmd);
                  ret = EINVAL;
                  goto ERROR;
          }
          SYSV_SHM_UNLOCK();
          return ret;
      }
    
  • Step 4: understand the binding / deletion after completion, gather and disperse well, and next time, mainly do the unmapping LOS in ShmDt_ Archmmuunmap is irrelevant without mapping. When the last unmapping process is detected, the shared memory ShmFreeSeg will be completely released
    /**
      * @brief When the operation on shared storage has ended, shmdt is called to separate from the storage segment
          If shmat is executed successfully, the kernel will make the shmid associated with the shared storage_ SHM in DS structure_ Nattch counter value minus 1
      * @attention Note: this does not remove shared storage identifiers and their associated data structures from the system. Shared storage still exists,
          Until a process with IPC_ The rmid command is called shmctl until the shared storage is specifically deleted
      * @param shmaddr 
      * @return INT32 
      */
      INT32 ShmDt(const VOID *shmaddr)
      {
          LosVmSpace *space = OsCurrProcessGet()->vmSpace;//Get process space
          (VOID)LOS_MuxAcquire(&space->regionMux);
          region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)shmaddr);//Find the linear region
          shmid = region->shmid;//Linear area sharing ID
          LOS_RbDelNode(&space->regionRbTree, &region->rbNode);//Remove nodes from red and black trees and linked lists
          LOS_ArchMmuUnmap(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);//Unmapping linear regions
          (VOID)LOS_MuxRelease(&space->regionMux);
          /* free it */
          free(region);//Free the memory in the memory pool occupied by the linear area
    
          SYSV_SHM_LOCK();
          seg = ShmFindSeg(shmid);//Find seg, the relationship between the linear region and the shared segment is 1:N, and the linear regions of other spaces will also be bound to the shared segment
          ShmPagesRefDec(seg);//Page references--
          seg->ds.shm_nattch--;//The number of processes using shared memory is one less
          if ((seg->ds.shm_nattch <= 0) && //No processes are using shared memory
              (seg->status & SHM_SEG_REMOVE)) {//When the status is delete, the physical page memory needs to be released, otherwise other processes will continue to use the shared memory
              ShmFreeSeg(seg);//Release the page frame memory in the seg page frame linked list, and then reset the seg state
          } else {
          seg->ds.shm_dtime = time(NULL);//Record the time of separation
          seg->ds.shm_lpid = LOS_GetCurrProcessID();//Record operation process ID
          }
          SYSV_SHM_UNLOCK();
    

summary

Here you should not ask about the role of shared memory and why it is the fastest way of inter process communication. If you still have these two questions, you should read them again: P. in addition, if you are careful, you will find that shared memory has a small disadvantage, that is, the problem of simultaneous access. Therefore, you need to use mutex to ensure that only one process is in use at the same time, SYSV_SHM_LOCK and SYSV_SHM_UNLOCK occurs in the above four steps.

STATIC LosMux g_sysvShmMux; //Mutex. Shared memory itself does not guarantee the synchronization of operations, so mutex is required
/* private macro */
#define SYSV_ SHM_ LOCK()     (VOID)LOS_ MuxLock(&g_sysvShmMux, LOS_WAIT_FOREVER) 	// Apply for permanent waiting lock
#define SYSV_ SHM_ UNLOCK()   (VOID)LOS_ MuxUnlock(&g_sysvShmMux) 	// Release lock

Bai Wen said that the core | grasp the main context

  • Baiwen is equivalent to touching out the muscle and organ system of the kernel, which makes people start to feel plump and three-dimensional. Because it starts directly from the annotation source code, it is often sorted out every experience in the annotation process, and the following articles are slowly formed. The content is based on the source code, often using life scenes as an analogy. Put the kernel knowledge points into a certain scene as much as possible, which has a sense of picture and is easy to understand and remember. It's important to say what others can understand! A hundred blogs are by no means Baidu's dogmatic talking about a bunch of awkward concepts, which is meaningless. More hope to make the kernel lifelike and feel more cordial.
  • Just as the code needs to be debug ged continuously, there will be many errors and omissions in the content of the article. Please forgive me, but it will be corrected repeatedly and updated continuously, V * XX represents the article serial number and the number of modifications. It is carefully crafted, concise and comprehensive, and strives to create high-quality content.
  • Bai Wen is posted at the "Hong Meng research station", "51CTO China CSDN", "the official account".

By function module:

Million note source code | buckle details everywhere

  • The purpose of annotating the core of millions of Chinese characters is to see its capillaries and cell structure clearly, which is equivalent to looking at the core with a magnifying glass. The kernel is not mysterious. It is easy to be addicted to find answers in the source code with questions. You will find that many articles interpret some problems incorrectly, or it is difficult to justify them if they are not profound. You will slowly form your own new interpretation, and the new interpretation will encounter new problems. You will advance layer by layer and roll forward. You are unwilling to let go with a magnifying glass.

  • < gitee | github | coding | codechina > The four big yards push the official source code, and the official account can be easily read back to millions.

Focus on not getting lost | code is life

Keywords: gitee harmonyos liteos

Added by careym1989 on Tue, 25 Jan 2022 09:59:51 +0200