/** * shmem_kernel_file_setup - get an unlinked file living in tmpfs which must be kernel internal. * @name: name for dentry (to be seen in /proc/<pid>/maps * @size: size to be set for the file * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size */struct file *shmem_kernel_file_setup(constchar*name,loff_t size,unsignedlong flags){return__shmem_file_setup(name, size, flags, S_PRIVATE);}staticstruct file *__shmem_file_setup(constchar*name,loff_t size,unsignedlong flags,unsignedint i_flags){struct file *res;struct inode *inode;struct path path;struct super_block *sb;struct qstr this;......this.name = name;this.len =strlen(name);this.hash =0; /* will go */ sb =shm_mnt->mnt_sb;path.mnt =mntget(shm_mnt);path.dentry =d_alloc_pseudo(sb,&this);d_set_d_op(path.dentry,&anon_ops);...... inode =shmem_get_inode(sb,NULL, S_IFREG | S_IRWXUGO,0, flags);inode->i_flags |= i_flags;d_instantiate(path.dentry, inode);inode->i_size = size;...... res =alloc_file(&path, FMODE_WRITE | FMODE_READ,&shmem_file_operations);return res;}
SYSCALL_DEFINE3(shmat,int, shmid,char __user *, shmaddr,int, shmflg){ unsignedlong ret; long err; err =do_shmat(shmid, shmaddr, shmflg,&ret, SHMLBA); force_successful_syscall_return(); return (long)ret;}longdo_shmat(int shmid,char __user *shmaddr,int shmflg, ulong *raddr,unsignedlong shmlba){struct shmid_kernel *shp;unsignedlong addr = (unsignedlong)shmaddr;unsignedlong size;struct file *file,*base;int err;unsignedlong flags = MAP_SHARED;unsignedlong prot;int acc_mode;struct ipc_namespace *ns;struct shm_file_data *sfd;int f_flags;unsignedlong populate =0;......if (shmflg & SHM_RDONLY) { prot = PROT_READ; acc_mode = S_IRUGO; f_flags = O_RDONLY; } else { prot = PROT_READ | PROT_WRITE; acc_mode = S_IRUGO | S_IWUGO; f_flags = O_RDWR; }if (shmflg & SHM_EXEC) { prot |= PROT_EXEC; acc_mode |= S_IXUGO; } /* * We cannot rely on the fs check since SYSV IPC does have an * additional creator id... */ ns =current->nsproxy->ipc_ns; shp =shm_obtain_object_check(ns, shmid);...... /* * We need to take a reference to the real shm file to prevent the * pointer from becoming stale in cases where the lifetime of the outer * file extends beyond that of the shm segment. It's not usually * possible, but it can happen during remap_file_pages() emulation as * that unmaps the memory, then does ->mmap() via file reference only. * We'll deny the ->mmap() if the shm segment was since removed, but to * detect shm ID reuse we need to compare the file pointers. */ base =get_file(shp->shm_file);shp->shm_nattch++; size =i_size_read(file_inode(base));ipc_unlock_object(&shp->shm_perm);rcu_read_unlock(); err =-ENOMEM; sfd =kzalloc(sizeof(*sfd), GFP_KERNEL);...... file =alloc_file_clone(base, f_flags, is_file_hugepages(base) ?&shm_file_operations_huge :&shm_file_operations);......sfd->id =shp->shm_perm.id;sfd->ns =get_ipc_ns(ns);sfd->file = base;sfd->vm_ops =NULL;file->private_data = sfd;...... addr =do_mmap_pgoff(file, addr, size, prot, flags,0,&populate,NULL);*raddr = addr; err =0;......}
staticintshm_mmap(struct file *file,struct vm_area_struct *vma){struct shm_file_data *sfd =shm_file_data(file);int ret; /* * In case of remap_file_pages() emulation, the file can represent an * IPC ID that was removed, and possibly even reused by another shm * segment already. Propagate this case as an error to caller. */ ret =__shm_open(vma);if (ret)return ret; ret =call_mmap(sfd->file, vma);if (ret) {shm_close(vma);return ret; }sfd->vm_ops =vma->vm_ops;vma->vm_ops =&shm_vm_ops;return0;}staticintshmem_mmap(struct file *file,struct vm_area_struct *vma){ file_accessed(file); vma->vm_ops =&shmem_vm_ops; return0;}
staticconststruct vm_operations_struct shm_vm_ops = { .open = shm_open, /* callback for a new vm-area open */ .close = shm_close, /* callback for when the vm-area is released */ .fault = shm_fault,};staticconststruct vm_operations_struct shmem_vm_ops = { .fault = shmem_fault, .map_pages = filemap_map_pages,};
staticintshm_fault(struct vm_fault *vmf){struct file *file =vmf->vma->vm_file;struct shm_file_data *sfd =shm_file_data(file);returnsfd->vm_ops->fault(vmf);}staticintshmem_fault(struct vm_fault *vmf){struct vm_area_struct *vma =vmf->vma;struct inode *inode =file_inode(vma->vm_file);gfp_t gfp =mapping_gfp_mask(inode->i_mapping);...... error =shmem_getpage_gfp(inode,vmf->pgoff,&vmf->page, sgp, gfp, vma, vmf,&ret);......}/* * shmem_getpage_gfp - find page in cache, or get from swap, or allocate * * If we allocate a new one we do not mark it dirty. That's up to the * vm. If we swap it in we mark it dirty since we also free the swap * entry since a page cannot live in both the swap and page cache. * * fault_mm and fault_type are only supplied by shmem_fault: * otherwise they are NULL. */staticintshmem_getpage_gfp(struct inode *inode,pgoff_t index,struct page **pagep,enum sgp_type sgp,gfp_t gfp,struct vm_area_struct *vma,struct vm_fault *vmf,int*fault_type){...... page =shmem_alloc_and_acct_page(gfp, info, sbinfo, index,false);......}
通过kvmalloc()在直接映射区分配struct sem_array结构体描述该信号量。在该结构体中会有多个信号量保存在struct sem sems[]中,通过semval表示当前信号量。
初始化sem_array和sems中的各个链表
通过ipc_addid()将创建的sem_array挂载到基数树上,并返回对应id
staticintnewary(struct ipc_namespace *ns,struct ipc_params *params){int retval;struct sem_array *sma;key_t key =params->key;int nsems =params->u.nsems;int semflg =params->flg;int i;...... sma =sem_alloc(nsems);......sma->sem_perm.mode = (semflg & S_IRWXUGO);sma->sem_perm.key = key;sma->sem_perm.security =NULL;......for (i =0; i < nsems; i++) {INIT_LIST_HEAD(&sma->sems[i].pending_alter);INIT_LIST_HEAD(&sma->sems[i].pending_const);spin_lock_init(&sma->sems[i].lock); }sma->complex_count =0;sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS;INIT_LIST_HEAD(&sma->pending_alter);INIT_LIST_HEAD(&sma->pending_const);INIT_LIST_HEAD(&sma->list_id);sma->sem_nsems = nsems;sma->sem_ctime =ktime_get_real_seconds(); /* ipc_addid() locks sma upon success. */ retval =ipc_addid(&sem_ids(ns),&sma->sem_perm,ns->sc_semmni);......ns->used_sems += nsems;sem_unlock(sma,-1);rcu_read_unlock();returnsma->sem_perm.id;}struct sem_array {struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */time64_t sem_ctime; /* create/last semctl() time */struct list_head pending_alter; /* pending operations */ /* that alter the array */struct list_head pending_const; /* pending complex operations */ /* that do not alter semvals */struct list_head list_id; /* undo requests on this array */int sem_nsems; /* no. of semaphores in array */int complex_count; /* pending complex operations */unsignedint use_global_lock;/* >0: global lock required */struct sem sems[];} __randomize_layout;struct sem {int semval; /* current value */ /* * PID of the process that last modified the semaphore. For * Linux, specifically these are: * - semop * - semctl, via SETVAL and SETALL. * - at task exit when performing undo adjustments (see exit_sem). */struct pid *sempid;spinlock_t lock; /* spinlock for fine-grained semtimedop */struct list_head pending_alter; /* pending single-sop operations */ /* that alter the semaphore */struct list_head pending_const; /* pending single-sop operations */ /* that do not alter the semaphore*/time64_t sem_otime; /* candidate for sem_otime */} ____cacheline_aligned_in_smp;