One hundred blog Analysis | this article is: (shared memory) | the fastest way of communication between processes
Related articles of process communication are:
- v26.08 Hongmeng kernel source code analysis (spin lock) | be a good comrade of chastity archway
- v27.05 Hongmeng kernel source code analysis (mutex lock) | it's also a lock. It's really fuller
- v28.04 Hongmeng kernel source code analysis (process communication) | nine inter process communication modes
- v29.05 Hongmeng kernel source code analysis (semaphore) | who is solving the synchronization between tasks
- v30.07 Hongmeng kernel source code analysis (event control) | how to synchronize many to many tasks
- v33.03 Hongmeng kernel source code analysis (message queue) | how to transfer big data asynchronously between processes
- v76.01 Hongmeng kernel source code analysis (shared memory) | the fastest communication mode between processes
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
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.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 };
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.//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; };
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#define SHM_SEG_FREE 0x2000 // Idle unused #define SHM_SEG_USED 0x4000 // Used #define SHM_SEG_REMOVE 0x8000 // delete
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, ®ion->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:
- Cause and effect > > General catalogue | Scheduling story | Memory master slave | Source code comments | Source code structure | Static site | Reference documents |
- Basic Tools > > Bidirectional linked list | Bitmap management | Stack mode | timer | Atomic operation | time management |
- Load run > > ELF format | ELF parsing | Static link | Relocation | Process Image |
- Process Management > > Process management | Process concept | Fork | Special process | Process recycling | Signal production | Signal consumption | Shell editing | Shell parsing |
- Compile build > > Compiling environment | Compilation process | Environment script | Build tools | gn application | ninja Ninja |
- Process communication > > Spin lock | mutex | Process communication | Semaphore | Event control | Message queue | Shared memory |
- Memory management > > memory allocation | memory management | Memory assembly | Memory mapping | Memory Rules | physical memory |
- Task management > > Clock task | task scheduling | task management | Scheduling queue | Scheduling mechanism | Thread concept | Concurrent parallel | CPU | system call | Task switching |
- File system > > Document concept | file system | Index node | Mount directory | Root file system | VFS | File handle | Pipeline file |
- Hardware Architecture > > Compilation basis | Assembly parameters | Working mode | register | Abnormal takeover | Compilation summary | Interrupt switching | Interrupt concept | Interrupt management |
- Device driver > > Character device | Console | Remote login |
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.