进程通信——IPC共享内存学习

Posted on 2011-04-01 17:30  margincc  阅读(2069)  评论(0编辑  收藏  举报

参考书籍:<linxu程序设计第2><linux系统分析和高级编程技术><linux内核完全注释>

共享内存即让两个进程访问同一部分逻辑内存。是有IPC为一个进程创建的特殊的地址范围,出现在进程的地址空间中,其他进程可以把同一段共享内存段连接到它们自己的地址空间去。如果一个进程向这段共享内存写了数据,所做的改变立刻被其他进程看到。但是共享内存本身没有同步功能,需要我们自己注意同步.

 1.共享内存相关结构体定义以及函数声明:

 系统的共享内存段在系统内核中也有一个内部的数据结构shmid_ds。描述了共享内存的认证,字节大小,分离、改变时间,创建它的进程,最后操作的进程以及多少进程在使用等信息。这些数据结构保存在 shm_segs 向量表中,struct shmid_ds不是内核用来跟踪共享内存区域的数据结构。取而代之的是,struct shmid_ds包含这种信息的绝大部分,而剩下的信息则位于下边要介绍的struct shmid_kernel中。Shmid_dsLinux/shm.h中有定义:

 

/*Obsolete, used only for backwards compatibility and libc5 compiles*/
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime;
/* last attach time */
__kernel_time_t shm_dtime;
/* last detach time */
__kernel_time_t shm_ctime;
/* last change time */
__kernel_ipc_pid_t shm_cpid;
/* pid of creator */
__kernel_ipc_pid_t shm_lpid;
/* pid of last operator */
unsigned
short shm_nattch; /* no. of current attaches */
unsigned
short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};

 

/*Obsolete, used only for backwards compatibility and libc5 compiles*/
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned
short seq;
};

相关头文件sys/shm.h sys/ipc.h sys/types.hipc/shm.c里有对应内核源码。这是shm.h里关于共享内存函数的声明。

 

    2.共享内存处理函数中使用的一些宏:系统中共享内存数量是限制宏和共享内存出来函数中使用的flag

 

#define SHMMAX 0x2000000 /* max shared seg size (bytes) */
#define SHMMIN 1 /* min shared seg size (bytes) */
#define SHMMNI 4096 /* max num of segs system wide */
#define SHMALL (SHMMAX/PAGE_SIZE*(SHMMNI/16)) /* max shm system wide (pages) */
#define SHMSEG SHMMNI /* max shared segs per process */

/* permission flag for shmget */
#define SHM_R 0400 /* or S_IRUGO from <linux/stat.h> */
#define SHM_W 0200 /* or S_IWUGO from <linux/stat.h> */

/* mode for attach */
#define SHM_RDONLY 010000 /* read-only access */
#define SHM_RND 020000 /* round attach address to SHMLBA boundary */
#define SHM_REMAP 040000 /* take-over region on attach */

/* super user shmctl commands */
#define SHM_LOCK 11
#define SHM_UNLOCK 12

/* ipcs ctl commands */
#define SHM_STAT 13
#define SHM_INFO 14

    在shm.c文件中 定义了共享内存例外的几个属性:

 

struct shmid_kernel /* private to the kernel */
{
struct kern_ipc_perm shm_perm;
struct file * shm_file;
int id;
unsigned
long shm_nattch;
unsigned
long shm_segsz;
time_t shm_atim;
time_t shm_dtim;
time_t shm_ctim;
pid_t shm_cprid;
pid_t shm_lprid;
struct mm_struct * shm_locker_mm;
pid_t shm_locker_pid;
};

#define shm_flags shm_perm.mode

static struct file_operations shm_file_operations;
static struct vm_operations_struct shm_vm_ops;

static struct ipc_ids shm_ids;

struct shmid_kernel用于分离“私有(private)”的共享内存相关信息和“公有(public)”的信息。struct shmid_ds里那些对用户应用程序可见的部分还保留在struct shmid _ds之内,而关系到内核的私有信息则位于struct shmid_kernel之内。用户应用程序需要能够通过struct shmid_ds来进行shmctl系统调用,所以它的定义必须对它们是可见的,但是内核私有实现的细节就不应该出现在struct的定义之中。否则,改变内核的执行可能会中断应用程序。

         3.共享内存函数解析:shmget():创建共享内存。程序需要一个键字参数key也就是共享内存段的名字,shmget返回一个供后续共享内存函数使用的共享内存标识码。首先系统是调用sys_ipcipc.c内)call值为SHMGET创建一个键值为key的共享内存对象,或者获得已经存在的键值为key的某共享内存对象的引用标识符。Size表需要共享的内存量,字节为单位。Shmflg9个权限标志。IPC_CREAT和其他标志或在一起才能创建新的共享内存段。使用方法和open函数的mode参数类似。

Sys_ipc函数:

 

asmlinkage int sys_ipc (uint call, int first, int second,
int third, void *ptr, long fifth)
{
int version, ret;

version
= call >> 16; /* hack for backward compatibility */
call
&= 0xffff;

switch (call) {
case SEMOP:
return sys_semop (first, (struct sembuf *)ptr, second);
case SEMGET:
return sys_semget (first, second, third);
case SEMCTL: {
union semun fourth;
if (!ptr)
return -EINVAL;
if (get_user(fourth.__pad, (void **) ptr))
return -EFAULT;
return sys_semctl (first, second, third, fourth);
}

case MSGSND:
return sys_msgsnd (first, (struct msgbuf *) ptr,
second, third);
case MSGRCV:
switch (version) {
case 0: {
struct ipc_kludge tmp;
if (!ptr)
return -EINVAL;

if (copy_from_user(&tmp,
(
struct ipc_kludge *) ptr,
sizeof (tmp)))
return -EFAULT;
return sys_msgrcv (first, tmp.msgp, second,
tmp.msgtyp, third);
}
default:
return sys_msgrcv (first,
(
struct msgbuf *) ptr,
second, fifth, third);
}
case MSGGET:
return sys_msgget ((key_t) first, second);
case MSGCTL:
return sys_msgctl (first, second, (struct msqid_ds *) ptr);

case SHMAT:
switch (version) {
default: {
ulong raddr;
ret
= sys_shmat (first, (char *) ptr, second, &raddr);
if (ret)
return ret;
return put_user (raddr, (ulong *) third);
}
case 1: /* iBCS2 emulator entry point */
if (!segment_eq(get_fs(), get_ds()))
return -EINVAL;
return sys_shmat (first, (char *) ptr, second, (ulong *) third);
}
case SHMDT:
return sys_shmdt ((char *)ptr);
case SHMGET:
return sys_shmget (first, second, third);
case SHMCTL:
return sys_shmctl (first, second,
(
struct shmid_ds *) ptr);
default:
return -EINVAL;
}
}

Sys_semget函数:

 

asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
{
struct shmid_kernel *shp;
int err, id = 0;

down(
&shm_ids.sem);
if (key == IPC_PRIVATE) {
err
= newseg(key, shmflg, size);
}
else if ((id = ipc_findkey(&shm_ids, key)) == -1) {
if (!(shmflg & IPC_CREAT))
err
= -ENOENT;
else
err
= newseg(key, shmflg, size);
}
else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {
err
= -EEXIST;
}
else {
shp
= shm_lock(id);
if(shp==NULL)
BUG();
if (shp->shm_segsz < size)
err
= -EINVAL;
else if (ipcperms(&shp->shm_perm, shmflg))
err
= -EACCES;
else
err
= shm_buildid(id, shp->shm_perm.seq);
shm_unlock(id);
}
up(
&shm_ids.sem);
return err;
}

         Sys_semget函数流程:Key=IPC_PRIVATE时穿件本进程的私有共享内存。Sys_shmgetnewseg函数与newquenewary相对应的函数,主要工作是计算size大小,申请shmid_kernel数据结构,申请一块用于建立页表的空间并清零,填写shmid_kernel数据结构并加入到shm_segs中,返回共享内存对象的引用标识符,struct shmid_kernel是由kmalloc分配的(在不可交换的内核内存里)。Key有值的时候,如果在没有在shm_segs中找到键值为key的共享内存对象,检测传入的shmflg,如果shmflg需要创建则创建,否则返回错误。如果找到,根据shmflg判断是否要求创建,要求创建返回错误,不要求创建并且检测shm_lock()返回的shmid_kernel结构体是否合法,合法返回对象的标识符。

键:在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。 Linux系统中的所有表示SystemVIPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找SystemVIPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。

Shmat()把建立的共享内存段连接到某个进程的地址空间以便访问。第一个参数shm_idshmget返回的标识码,shm_addr是共享内存连接到当前进程时准备放置的地址,如果是空指针表把该工作交给系统完成。Shmflg处理标识,SHM_RNDSHM_RDONLY。返回的是进程中虚拟地址。

Shmat()函数:

 

asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
{
struct shmid_kernel *shp;
unsigned
long addr;
unsigned
long size;
struct file * file;
int err;
unsigned
long flags;
unsigned
long prot;
unsigned
long o_flags;
int acc_mode;
void *user_addr;

if (shmid < 0)
return -EINVAL;

if ((addr = (ulong)shmaddr)) {
if (addr & (SHMLBA-1)) {
if (shmflg & SHM_RND)
addr
&= ~(SHMLBA-1); /* round down */
else
return -EINVAL;
}
flags
= MAP_SHARED | MAP_FIXED;
}
else {
if ((shmflg & SHM_REMAP))
return -EINVAL;

flags
= MAP_SHARED;
}

if (shmflg & SHM_RDONLY) {
prot
= PROT_READ;
o_flags
= O_RDONLY;
acc_mode
= S_IRUGO;
}
else {
prot
= PROT_READ | PROT_WRITE;
o_flags
= O_RDWR;
acc_mode
= S_IRUGO | S_IWUGO;
}

/*
* We cannot rely on the fs check since SYSV IPC does have an
* additional creator id...
*/
shp
= shm_lock(shmid);
if(shp == NULL)
return -EINVAL;
err
= shm_checkid(shp,shmid);
if (err) {
shm_unlock(shmid);
return err;
}
if (ipcperms(&shp->shm_perm, acc_mode)) {
shm_unlock(shmid);
return -EACCES;
}
file
= shp->shm_file;
size
= file->f_dentry->d_inode->i_size;
shp
->shm_nattch++;
shm_unlock(shmid);

down_write(¤t
->mm->mmap_sem);
if (addr && !(shmflg & SHM_REMAP)) {
user_addr
= ERR_PTR(-EINVAL);
if (find_vma_intersection(current->mm, addr, addr + size))
goto invalid;
/*
* If shm segment goes below stack, make sure there is some
* space left for the stack to grow (at least 4 pages).
*/
if (addr < current->mm->start_stack &&
addr
> current->mm->start_stack - size - PAGE_SIZE * 5)
goto invalid;
}

user_addr
= (void*) do_mmap (file, addr, size, prot, flags, 0);

invalid:
up_write(¤t
->mm->mmap_sem);

down (
&shm_ids.sem);
if(!(shp = shm_lock(shmid)))
BUG();
shp
->shm_nattch--;
if(shp->shm_nattch == 0 &&
shp
->shm_flags & SHM_DEST)
shm_destroy (shp);
else
shm_unlock(shmid);
up (
&shm_ids.sem);

*raddr = (unsigned long) user_addr;
err
= 0;
if (IS_ERR(user_addr))
err
= PTR_ERR(user_addr);
return err;

}

Kernelsys_shmat()函数首先是根据shmid找到共享内存对象。根据shmaddr的地址座位映射的虚拟地址,当shmaddr0时,随机分配一个。检查虚拟地址合法性,申请一块用来建vm_area_struct结构内存,并填写,加入到进程的mm结构和该共享内存对象的vm_area_struct队列中。

Shmdt函数时把共享内存与当前进程脱离开。参数是shmget返回的地址指针。不影响其他进程和该共享内存对象的交互。当前进程的vm_area_struct结构被从shmid_ds中删除释放,进程的页表也被更新,当该共享内存和最后一个进程也分离,则共享内存页和kemid_kernel结构也都被释放。

Sys_shmdt函数: 

 

asmlinkage long sys_shmdt (char *shmaddr)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *shmd, *shmdnext;
int retval = -EINVAL;

down_write(
&mm->mmap_sem);
for (shmd = mm->mmap; shmd; shmd = shmdnext) {
shmdnext
= shmd->vm_next;
if ((shmd->vm_ops == &shm_vm_ops || is_vm_hugetlb_page(shmd))
&& shmd->vm_start - (shmd->vm_pgoff << PAGE_SHIFT) == (ulong) shmaddr) {
do_munmap(mm, shmd
->vm_start, shmd->vm_end - shmd->vm_start, 1);
retval
= 0;
}
}
up_write(
&mm->mmap_sem);
return retval;
}

Shmctl函数,共享内存的控制函数。参数command表将要采取的动作:IPC_STAT,SHM_STAT shmid_ds结构中的数据设置为共享内存的当前关联值,IPC_SET在进程有权限下把共享内存的当前关联值设为shmid_ds给的值,IPC_RMID删除共享内存段,获得共享内存状态(SHM_INFO IPC_INFO),锁定和释放(SHM_LOCKED)等。参数buf是指向shmid_ds结构体的指针。

shmctl函数:

 

asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
{
struct shm_setbuf setbuf;
struct shmid_kernel *shp;
int err, version;

if (cmd < 0 || shmid < 0)
return -EINVAL;

version
= ipc_parse_version(&cmd);

switch (cmd) { /* replace with proc interface ? */
case IPC_INFO:
{
struct shminfo64 shminfo;

memset(
&shminfo,0,sizeof(shminfo));
shminfo.shmmni
= shminfo.shmseg = shm_ctlmni;
shminfo.shmmax
= shm_ctlmax;
shminfo.shmall
= shm_ctlall;

shminfo.shmmin
= SHMMIN;
if(copy_shminfo_to_user (buf, &shminfo, version))
return -EFAULT;
/* reading a integer is always atomic */
err
= shm_ids.max_id;
if(err<0)
err
= 0;
return err;
}
case SHM_INFO:
{
struct shm_info shm_info;

memset(
&shm_info,0,sizeof(shm_info));
down(
&shm_ids.sem);
shm_info.used_ids
= shm_ids.in_use;
shm_get_stat (
&shm_info.shm_rss, &shm_info.shm_swp);
shm_info.shm_tot
= shm_tot;
shm_info.swap_attempts
= 0;
shm_info.swap_successes
= 0;
err
= shm_ids.max_id;
up(
&shm_ids.sem);
if(copy_to_user (buf, &shm_info, sizeof(shm_info)))
return -EFAULT;

return err < 0 ? 0 : err;
}
case SHM_STAT:
case IPC_STAT:
{
struct shmid64_ds tbuf;
int result;
memset(
&tbuf, 0, sizeof(tbuf));
shp
= shm_lock(shmid);
if(shp==NULL)
return -EINVAL;
if(cmd==SHM_STAT) {
err
= -EINVAL;
if (shmid > shm_ids.max_id)
goto out_unlock;
result
= shm_buildid(shmid, shp->shm_perm.seq);
}
else {
err
= shm_checkid(shp,shmid);
if(err)
goto out_unlock;
result
= 0;
}
err
=-EACCES;
if (ipcperms (&shp->shm_perm, S_IRUGO))
goto out_unlock;
kernel_to_ipc64_perm(
&shp->shm_perm, &tbuf.shm_perm);
tbuf.shm_segsz
= shp->shm_segsz;
tbuf.shm_atime
= shp->shm_atim;
tbuf.shm_dtime
= shp->shm_dtim;
tbuf.shm_ctime
= shp->shm_ctim;
tbuf.shm_cpid
= shp->shm_cprid;
tbuf.shm_lpid
= shp->shm_lprid;
tbuf.shm_nattch
= shp->shm_nattch;
shm_unlock(shmid);
if(copy_shmid_to_user (buf, &tbuf, version))
return -EFAULT;
return result;
}
case SHM_LOCK:
case SHM_UNLOCK:
{
/* Allow superuser to lock segment in memory */
if (!can_do_mlock())
return -EPERM;
shp
= shm_lock(shmid);
if(shp==NULL)
return -EINVAL;
err
= shm_checkid(shp,shmid);
if(err)
goto out_unlock;
if (current->euid != shp->shm_perm.uid &&
current
->euid != shp->shm_perm.cuid &&
!capable(CAP_IPC_LOCK)) {
err
= -EPERM;
goto out_unlock;
}
if(cmd==SHM_LOCK) {
err
= 0;
if (!is_file_hugepages(shp->shm_file))
err
= shmem_lock(shp->shm_file, 1,
&shp->shm_locker_mm,
&shp->shm_locker_pid);
if (!err)
shp
->shm_flags |= SHM_LOCKED;
}
else {
if (!is_file_hugepages(shp->shm_file))
shmem_lock(shp
->shm_file, 0,
&shp->shm_locker_mm,
&shp->shm_locker_pid);
shp
->shm_flags &= ~SHM_LOCKED;
}
shm_unlock(shmid);
return err;
}
case IPC_RMID:
{
/*
* We cannot simply remove the file. The SVID states
* that the block remains until the last person
* detaches from it, then is deleted. A shmat() on
* an RMID segment is legal in older Linux and if
* we change it apps break...
*
* Instead we set a destroyed flag, and then blow
* the name away when the usage hits zero.
*/
down(
&shm_ids.sem);
shp
= shm_lock(shmid);
err
= -EINVAL;
if (shp == NULL)
goto out_up;
err
= shm_checkid(shp, shmid);
if(err)
goto out_unlock_up;
if (current->euid != shp->shm_perm.uid &&
current
->euid != shp->shm_perm.cuid &&
!capable(CAP_SYS_ADMIN)) {
err
=-EPERM;
goto out_unlock_up;
}
if (shp->shm_nattch){
shp
->shm_flags |= SHM_DEST;
/* Do not find it any more */
shp
->shm_perm.key = IPC_PRIVATE;
shm_unlock(shmid);
}
else
shm_destroy (shp);
up(
&shm_ids.sem);
return err;
}

case IPC_SET:
{
if(copy_shmid_from_user (&setbuf, buf, version))
return -EFAULT;
down(
&shm_ids.sem);
shp
= shm_lock(shmid);
err
=-EINVAL;
if(shp==NULL)
goto out_up;
err
= shm_checkid(shp,shmid);
if(err)
goto out_unlock_up;
err
=-EPERM;
if (current->euid != shp->shm_perm.uid &&
current
->euid != shp->shm_perm.cuid &&
!capable(CAP_SYS_ADMIN)) {
goto out_unlock_up;
}

shp
->shm_perm.uid = setbuf.uid;
shp
->shm_perm.gid = setbuf.gid;
shp
->shm_flags = (shp->shm_flags & ~S_IRWXUGO)
| (setbuf.mode & S_IRWXUGO);
shp
->shm_ctim = CURRENT_TIME;
break;
}

default:
return -EINVAL;
}

err
= 0;
out_unlock_up:
shm_unlock(shmid);
out_up:
up(
&shm_ids.sem);
return err;
out_unlock:
shm_unlock(shmid);
return err;
}

实例(linux程序设计源码):

shm_com.h

 

/* A common header file to describe the shared memory we wish to pass about. */

#define TEXT_SZ 2048

struct shared_use_st {
int written_by_you;
char some_text[TEXT_SZ];
};

shm1.c

 

/* Our first program is a consumer. After the headers the shared memory segment
(the size of our shared memory structure) is created with a call to shmget,
with the IPC_CREAT bit specified.
*/

#include
<unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>

#include
<sys/types.h>
#include
<sys/ipc.h>
#include
<sys/shm.h>

#include
"shm_com.h"

int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid;

srand((unsigned
int)getpid());

shmid
= shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

if (shmid == -1) {
fprintf(stderr,
"shmget failed\n");
exit(EXIT_FAILURE);
}

/* We now make the shared memory accessible to the program. */

shared_memory
= shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr,
"shmat failed\n");
exit(EXIT_FAILURE);
}

printf(
"Memory attached at %X\n", (int)shared_memory);

/* The next portion of the program assigns the shared_memory segment to shared_stuff,
which then prints out any text in written_by_you. The loop continues until end is found
in written_by_you. The call to sleep forces the consumer to sit in its critical section,
which makes the producer wait.
*/

shared_stuff
= (struct shared_use_st *)shared_memory;
shared_stuff
->written_by_you = 0;
while(running) {
if (shared_stuff->written_by_you) {
printf(
"You wrote: %s", shared_stuff->some_text);
sleep( rand()
% 4 ); /* make the other process wait for us ! */
shared_stuff
->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
running
= 0;
}
}
}

/* Lastly, the shared memory is detached and then deleted. */

if (shmdt(shared_memory) == -1) {
fprintf(stderr,
"shmdt failed\n");
exit(EXIT_FAILURE);
}

if (shmctl(shmid, IPC_RMID, 0) == -1) {
fprintf(stderr,
"shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}

shm2.c

 

/* The second program is the producer and allows us to enter data for consumers.
It's very similar to shm1.c and looks like this.
*/

#include
<unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>

#include
<sys/types.h>
#include
<sys/ipc.h>
#include
<sys/shm.h>

#include
"shm_com.h"

int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;

shmid
= shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

if (shmid == -1) {
fprintf(stderr,
"shmget failed\n");
exit(EXIT_FAILURE);
}

shared_memory
= shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr,
"shmat failed\n");
exit(EXIT_FAILURE);
}

printf(
"Memory attached at %X\n", (int)shared_memory);

shared_stuff
= (struct shared_use_st *)shared_memory;
while(running) {
while(shared_stuff->written_by_you == 1) {
sleep(
1);
printf(
"waiting for client...\n");
}
printf(
"Enter some text: ");
fgets(buffer, BUFSIZ, stdin);

strncpy(shared_stuff
->some_text, buffer, TEXT_SZ);
shared_stuff
->written_by_you = 1;

if (strncmp(buffer, "end", 3) == 0) {
running
= 0;
}
}

if (shmdt(shared_memory) == -1) {
fprintf(stderr,
"shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

 

实例的共享内存大小为shared_use_st结构体大小,shm1.c shm2.c 通过结构体第一个变量wtiteen_by_you实现两个进程对共享内存区域的同步操作。读写的内容保存到buffer中。首先创建共享内存段,连接。然后shm1.c持续读共享内存中内容,shm2.c持续写共享到共享内存中,以字符串end结束。共享内存从进程分离并删除。

用两个shh登陆分别运行1和2程序:


Ipcs –mipcrm shm <id>命令:

ipcs -m 可以看我们的系统共享内存情况,ipcrm shm <id>可以删除共享内存。运行1程序后直接ctrl c中止进程,我们就没有释放申请的共享内存段,我们用ipcrm shm释放共享内存。

 

   

Copyright © 2024 margincc
Powered by .NET 9.0 on Kubernetes