📄 shm.c
字号:
/* * linux/ipc/shm.c * Copyright (C) 1992, 1993 Krishna Balasubramanian * Many improvements/fixes by Bruno Haible. * assume user segments start at 0x0 */#include <linux/errno.h>#include <asm/segment.h>#include <linux/sched.h>#include <linux/ipc.h> #include <linux/shm.h>#include <linux/stat.h>#include <linux/malloc.h>extern int ipcperms (struct ipc_perm *ipcp, short semflg);extern unsigned int get_swap_page(void);static int findkey (key_t key);static int newseg (key_t key, int shmflg, int size);static int shm_map (struct shm_desc *shmd, int remap);static void killseg (int id);static int shm_tot = 0; /* total number of shared memory pages */static int shm_rss = 0; /* number of shared memory pages that are in memory */static int shm_swp = 0; /* number of shared memory pages that are in swap */static int max_shmid = 0; /* every used id is <= max_shmid */static struct wait_queue *shm_lock = NULL;static struct shmid_ds *shm_segs[SHMMNI];static unsigned short shm_seq = 0; /* incremented, for recognizing stale ids *//* some statistics */static ulong swap_attempts = 0;static ulong swap_successes = 0;static ulong used_segs = 0;void shm_init (void){ int id; for (id = 0; id < SHMMNI; id++) shm_segs[id] = (struct shmid_ds *) IPC_UNUSED; shm_tot = shm_rss = shm_seq = max_shmid = used_segs = 0; shm_lock = NULL; return;}static int findkey (key_t key) { int id; struct shmid_ds *shp; for (id=0; id <= max_shmid; id++) { while ((shp = shm_segs[id]) == IPC_NOID) sleep_on (&shm_lock); if (shp == IPC_UNUSED) continue; if (key == shp->shm_perm.key) return id; } return -1;}/* * allocate new shmid_ds and pgtable. protected by shm_segs[id] = NOID. */static int newseg (key_t key, int shmflg, int size){ struct shmid_ds *shp; int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; int id, i; if (size < SHMMIN) return -EINVAL; if (shm_tot + numpages >= SHMALL) return -ENOSPC; for (id=0; id < SHMMNI; id++) if (shm_segs[id] == IPC_UNUSED) { shm_segs[id] = (struct shmid_ds *) IPC_NOID; goto found; } return -ENOSPC;found: shp = (struct shmid_ds *) kmalloc (sizeof (*shp), GFP_KERNEL); if (!shp) { shm_segs[id] = (struct shmid_ds *) IPC_UNUSED; if (shm_lock) wake_up (&shm_lock); return -ENOMEM; } shp->shm_pages = (ulong *) kmalloc (numpages*sizeof(ulong),GFP_KERNEL); if (!shp->shm_pages) { shm_segs[id] = (struct shmid_ds *) IPC_UNUSED; if (shm_lock) wake_up (&shm_lock); kfree_s (shp, sizeof (*shp)); return -ENOMEM; } for (i=0; i< numpages; shp->shm_pages[i++] = 0); shm_tot += numpages; shp->shm_perm.key = key; shp->shm_perm.mode = (shmflg & S_IRWXUGO); shp->shm_perm.cuid = shp->shm_perm.uid = current->euid; shp->shm_perm.cgid = shp->shm_perm.gid = current->egid; shp->shm_perm.seq = shm_seq; shp->shm_segsz = size; shp->shm_cpid = current->pid; shp->attaches = NULL; shp->shm_lpid = shp->shm_nattch = 0; shp->shm_atime = shp->shm_dtime = 0; shp->shm_ctime = CURRENT_TIME; shp->shm_npages = numpages; if (id > max_shmid) max_shmid = id; shm_segs[id] = shp; used_segs++; if (shm_lock) wake_up (&shm_lock); return id + (int)shm_seq*SHMMNI;}int sys_shmget (key_t key, int size, int shmflg){ struct shmid_ds *shp; int id = 0; if (size < 0 || size > SHMMAX) return -EINVAL; if (key == IPC_PRIVATE) return newseg(key, shmflg, size); if ((id = findkey (key)) == -1) { if (!(shmflg & IPC_CREAT)) return -ENOENT; return newseg(key, shmflg, size); } if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) return -EEXIST; shp = shm_segs[id]; if (shp->shm_perm.mode & SHM_DEST) return -EIDRM; if (size > shp->shm_segsz) return -EINVAL; if (ipcperms (&shp->shm_perm, shmflg)) return -EACCES; return shp->shm_perm.seq*SHMMNI + id;}/* * Only called after testing nattch and SHM_DEST. * Here pages, pgtable and shmid_ds are freed. */static void killseg (int id){ struct shmid_ds *shp; int i, numpages; ulong page; shp = shm_segs[id]; if (shp == IPC_NOID || shp == IPC_UNUSED) { printk ("shm nono: killseg called on unused seg id=%d\n", id); return; } shp->shm_perm.seq++; /* for shmat */ numpages = shp->shm_npages; shm_seq++; shm_segs[id] = (struct shmid_ds *) IPC_UNUSED; used_segs--; if (id == max_shmid) while (max_shmid && (shm_segs[--max_shmid] == IPC_UNUSED)); if (!shp->shm_pages) { printk ("shm nono: killseg shp->pages=NULL. id=%d\n", id); return; } for (i=0; i< numpages ; i++) { if (!(page = shp->shm_pages[i])) continue; if (page & 1) { free_page (page & PAGE_MASK); shm_rss--; } else { swap_free (page); shm_swp--; } } kfree_s (shp->shm_pages, numpages * sizeof (ulong)); shm_tot -= numpages; kfree_s (shp, sizeof (*shp)); return;}int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf){ struct shmid_ds *shp, tbuf; struct ipc_perm *ipcp; int id, err; if (cmd < 0 || shmid < 0) return -EINVAL; if (cmd == IPC_SET) { if (!buf) return -EFAULT; err = verify_area (VERIFY_READ, buf, sizeof (*buf)); if (err) return err; memcpy_fromfs (&tbuf, buf, sizeof (*buf)); } switch (cmd) { /* replace with proc interface ? */ case IPC_INFO: { struct shminfo shminfo; if (!buf) return -EFAULT; shminfo.shmmni = SHMMNI; shminfo.shmmax = SHMMAX; shminfo.shmmin = SHMMIN; shminfo.shmall = SHMALL; shminfo.shmseg = SHMSEG; err = verify_area (VERIFY_WRITE, buf, sizeof (struct shminfo)); if (err) return err; memcpy_tofs (buf, &shminfo, sizeof(struct shminfo)); return max_shmid; } case SHM_INFO: { struct shm_info shm_info; if (!buf) return -EFAULT; err = verify_area (VERIFY_WRITE, buf, sizeof (shm_info)); if (err) return err; shm_info.used_ids = used_segs; shm_info.shm_rss = shm_rss; shm_info.shm_tot = shm_tot; shm_info.shm_swp = shm_swp; shm_info.swap_attempts = swap_attempts; shm_info.swap_successes = swap_successes; memcpy_tofs (buf, &shm_info, sizeof(shm_info)); return max_shmid; } case SHM_STAT: if (!buf) return -EFAULT; err = verify_area (VERIFY_WRITE, buf, sizeof (*shp)); if (err) return err; if (shmid > max_shmid) return -EINVAL; shp = shm_segs[shmid]; if (shp == IPC_UNUSED || shp == IPC_NOID) return -EINVAL; if (ipcperms (&shp->shm_perm, S_IRUGO)) return -EACCES; id = shmid + shp->shm_perm.seq * SHMMNI; memcpy_tofs (buf, shp, sizeof(*shp)); return id; } shp = shm_segs[id = shmid % SHMMNI]; if (shp == IPC_UNUSED || shp == IPC_NOID) return -EINVAL; ipcp = &shp->shm_perm; if (ipcp->seq != shmid / SHMMNI) return -EIDRM; switch (cmd) { case SHM_UNLOCK: if (!suser()) return -EPERM; if (!(ipcp->mode & SHM_LOCKED)) return -EINVAL; ipcp->mode &= ~SHM_LOCKED; break; case SHM_LOCK:/* Allow superuser to lock segment in memory *//* Should the pages be faulted in here or leave it to user? *//* need to determine interaction with current->swappable */ if (!suser()) return -EPERM; if (ipcp->mode & SHM_LOCKED) return -EINVAL; ipcp->mode |= SHM_LOCKED; break; case IPC_STAT: if (ipcperms (ipcp, S_IRUGO)) return -EACCES; if (!buf) return -EFAULT; err = verify_area (VERIFY_WRITE, buf, sizeof (*shp)); if (err) return err; memcpy_tofs (buf, shp, sizeof(*shp)); break; case IPC_SET: if (suser() || current->euid == shp->shm_perm.uid || current->euid == shp->shm_perm.cuid) { ipcp->uid = tbuf.shm_perm.uid; ipcp->gid = tbuf.shm_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (tbuf.shm_perm.mode & S_IRWXUGO); shp->shm_ctime = CURRENT_TIME; break; } return -EPERM; case IPC_RMID: if (suser() || current->euid == shp->shm_perm.uid || current->euid == shp->shm_perm.cuid) { shp->shm_perm.mode |= SHM_DEST; if (shp->shm_nattch <= 0) killseg (id); break; } return -EPERM; default: return -EINVAL; } return 0;}/* * check range is unmapped, ensure page tables exist * mark page table entries with shm_sgn. * if remap != 0 the range is remapped. */static int shm_map (struct shm_desc *shmd, int remap){ unsigned long invalid = 0; unsigned long *page_table; unsigned long tmp, shm_sgn; unsigned long page_dir = shmd->task->tss.cr3; /* check that the range is unmapped and has page_tables */ for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE) { page_table = PAGE_DIR_OFFSET(page_dir,tmp); if (*page_table & PAGE_PRESENT) { page_table = (ulong *) (PAGE_MASK & *page_table); page_table += ((tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1)); if (*page_table) { if (!remap) return -EINVAL; if (*page_table & PAGE_PRESENT) { --current->rss; free_page (*page_table & PAGE_MASK); } else swap_free (*page_table); invalid++; } continue; } { unsigned long new_pt; if(!(new_pt = get_free_page(GFP_KERNEL))) /* clearing needed? SRB. */ return -ENOMEM; *page_table = new_pt | PAGE_TABLE; tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE); }} if (invalid) invalidate(); /* map page range */ shm_sgn = shmd->shm_sgn; for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE, shm_sgn += (1 << SHM_IDX_SHIFT)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -