精华内容
下载资源
问答
  • 共享内存使用方法

    2017-07-29 11:14:21
    可以通过命令 ipcs -a查看所有的IPC对象使用情况 ipcs -m 查看共享内存 ipcs -q 查看消息队列 ...共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝 为了
    可以通过命令
    ipcs -a查看所有的IPC对象使用情况
    ipcs -m 查看共享内存
    ipcs -q 查看消息队列
    ipcs -s 查看信号灯集
    ipcrm -m shmid 删除shmid的共享内存
    ipcrm -q msgid 删除消息队列
    ipcrm -s semid 删除信号灯集
    共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
    为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
    进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高效率
    由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。
    int shmget(key_t key, size_t size, int shmflg);
    功能:创建或打开一段共享内存
    参数:
    key:IPC_PRIVATE代表创建私有共享内存(创建出来的共享内存的key值为0),每次创建都会创建新的共享内存。传入其他key值,可以使其他进程通过key值来得到同一个共享内存。
    size:申请共享内存的大小
    shmflg:PIC_CREAT创建共享内存|0666、PIC_EXCL如果存在报错
    返回值:成功返回创建的共享内存的shmid,失败返回-1并且设置errno。
    如:shm_id=shmget(ftok("/",'a'),128,PIC_CREAT|PIC_EXCL|0666);
    创建过之后在打开就可以shm_id=shmget(ftok("/",'a'),0,0);因为之前已经创建好了,所以写0就可以了。当然这样也不会错shm_id=shmget(ftok("/",'a'),128,0666);
    key_t ftok(const char *pathname, int proj_id);
    功能:获取键值
    参数:
    pathname:路径
    proj_id:尽量填写一个字符(0-255)
    返回值:成功返回key值,失败返回:-1
    ftok()函数会获取第一个参数(文件或目录)的属性信息,并把ftok()的第二个参数的后8位,st_dev的后两位,st_ino的后四位,构成了一个键值。
    这个函数在信号灯集里面提及过了,这里就不详细说明了。
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:映射共享内存
    参数:shmid:共享内存ID
    shmaddr:一般设为NULL,操作系统帮我们选择,否则需要自定义映射地址
    shmflg:SHM_RDONLY为只读,0表示可读可写
    返回值:成功返回映射后的地址,失败返回-1
    char *p=shmat(shm_id,NULL,0);
    int shmdt(const void *shmaddr);
    功能:取消映射共享内存
    参数:取消映射的地址
    返回值:成功返回0,失败返回-1;
    shmdt(p);
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    功能:对共享内存进行操作
    参数:shmid:共享内存的ID
    cmd:IPC_STAT表示获取共享内存的信息
    IPC_SET表示设置共享内存的信息
    IPC_RMID表示删除共享内存
    返回值:成功返回0,失败返回-1
    struct shmid_ds {
    struct ipc_perm shm_perm; /* Ownership and permissions */
    size_t shm_segsz; /* Size of segment (bytes) */
    time_t shm_atime; /* Last attach time */
    time_t shm_dtime; /* Last detach time */
    time_t shm_ctime; /* Last change time */
    pid_t shm_cpid; /* PID of creator */
    pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
    shmatt_t shm_nattch; /* No. of current attaches */
    ...
    };
    例如:shmctl(shm_id,IPC_RMID,NULL);

    在还有进程映射共享内存的情况下,对共享内存进行IPC_RMID操作会使这段共享内存变为dest状态,key值变为0,当nattch映射数为0的时候自动被删除。
    共享内存结合信号的代码如下:



    展开全文
  • Android提供了一个高效共享内存机制。如果应用中涉及到在多个进程间交换数据时使用Android提高的共享内存机制将会大大的提高效率。但是也许是出于安全考虑,在应用层使用共享内存机制将会遇到很多障碍,这篇文章...

    Android提供了一个高效的共享内存机制。如果应用中涉及到在多个进程间交换数据时使用Android提高的共享内存机制将会大大的提高效率。但是也许是出于安全考虑,在应用层使用共享内存机制将会遇到很多障碍,这篇文章旨在解决这些障碍

    frameworks/base/core/java/android/os/MemoryFile.java的源码位置下面是5.0版本的网上源码

    MemoryFile.java

    使用共享内存的流程大概是:

    在一个进程中创建一个共享内存。在Android应用层中,用MemoryFile描述一块共享内存,创建共享内存其实就是创建一MemoryFile对象。这一步非常简单,MemoryFile提供了相应的构造函数

    public MemoryFile(String name, int length) throws IOException

    因为这块内存需要让其他进程共享,所以需要取得刚刚创建的共享内存的文件描述符FileDescriptor,并且把序列化成ParcelFileDescriptor。对于获取FileDescriptor,MemoryFile提供了相应的方法

    /**
         * Gets a FileDescriptor for the memory file.
         *
         * The returned file descriptor is not duplicated.
         *
         * @throws IOException If the memory file has been closed.
         *
         * @hide
         */
        public FileDescriptor getFileDescriptor() throws IOException {
            return mFD;
        }

    @hide可见这个方法是隐藏的,在应用层没法直接调用。所以需要用反射来完成,具体代码如下

    /**
         * 获取memoryFile的FileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return 这块共享内存对应的文件描述符
         */
        public static FileDescriptor getFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            FileDescriptor fd;
            fd = (FileDescriptor) ReflectUtil.invoke("android.os.MemoryFile",memoryFile,"getFileDescriptor");
            return fd;
        }

    对于把FileDescriptor序列化成ParcelFileDescriptor,ParcelFileDescriptor也提供了一隐藏的构造函数:

    /** {@hide} */
        public ParcelFileDescriptor(FileDescriptor fd) {
            this(fd, null);
        }

    所以我们还是可以通过反射来实现:

    /**
         * 获取memoryFile的ParcelFileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return ParcelFileDescriptor
         */
        public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            ParcelFileDescriptor pfd;
            FileDescriptor fd = getFileDescriptor(memoryFile);
            pfd = (ParcelFileDescriptor) ReflectUtil.getInstance("android.os.ParcelFileDescriptor",fd);
            return pfd;
        }

    把刚刚得到的ParcelFileDescriptor传递到其他进程,这个比较简单直接用binder传就可以了

    通过描述共享内存文件描述取得一个描述共享内存的MemoryFile对象,并且需要让这个MemoryFile对象指向刚刚创建的共享内存。在低版本的系统中存在一个构造函数可以直接以FileDescriptor为参数构造出一个MemoryFile对象,这样构造出来的对象刚好指向FileDescriptor描述的共享内存。但是在高版本中没有样的构造函数了。所以在这里我利用了一个取巧的方式。思路是:利用构造函数 

    public MemoryFile(String name, int length) throws IOException 

    构造一个MemoryFile对象,当然此时也创建了一块新的共享内存,但是这块共享内存不是我们需要的;调用public void close()方法关闭刚刚创建的共享内存。通过前面的操作后我们得到了一个MemoryFile对象,但是这个对象没有指向任何共享内存,所以接下来我们就需要让MemoryFile对象指向我们需要的共享内存,也就是FileDescriptor描述的那块。在MemoryFile中有一个native方法:

    private static native long native_mmap(FileDescriptor fd, int length, int mode)

    这个方法就是把fd描述的共享内存映射到虚拟地址空间中。所以我们可以已刚刚获得的FileDescriptor 作为参数利用反射调用这个方法:

    /**
         * 打开共享内存,一般是一个地方创建了一块共享内存
         * 另一个地方持有描述这块共享内存的文件描述符,调用
         * 此方法即可获得一个描述那块共享内存的MemoryFile
         * 对象
         * @param fd 文件描述
         * @param length 共享内存的大小
         * @param mode PROT_READ = 0x1只读方式打开,
         *             PROT_WRITE = 0x2可写方式打开,
         *             PROT_WRITE|PROT_READ可读可写方式打开
         * @return MemoryFile
         */
        public static MemoryFile openMemoryFile(FileDescriptor fd,int length,int mode){
            MemoryFile memoryFile = null;
            try {
                memoryFile = new MemoryFile("tem",1);
                memoryFile.close();
                Class<?> c = MemoryFile.class;
                Method native_mmap = null;
                Method[] ms = c.getDeclaredMethods();
                for(int i = 0;ms != null&&i<ms.length;i++){
                    if(ms[i].getName().equals("native_mmap")){
                        native_mmap = ms[i];
                    }
                }
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mFD", fd);
                ReflectUtil.setField("android.os.MemoryFile",memoryFile,"mLength",length);
                long address = (long) ReflectUtil.invokeMethod( null, native_mmap, fd, length, mode);
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return memoryFile;
        }

    这样我们就得到了一个指向一开始我们创建的那块共享内存的MemoryFile了,接下来就可以调用它的public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)和public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)从共享内存中读数据和往共享内存中写数据了

    最后上完整的代码:

    package wzr.com.slidefinish.util;
    
    import android.os.MemoryFile;
    import android.os.ParcelFileDescriptor;
    
    import java.io.FileDescriptor;
    import java.io.IOException;
    import java.lang.reflect.Method;
    
    /**
     * 对memoryFile类的扩展
     * 1.从memoryFile对象中获取FileDescriptor,ParcelFileDescriptor
     * 2.根据一个FileDescriptor和文件length实例化memoryFile对象
     */
    public class MemoryFileHelper {
        /**
         * 创建共享内存对象
          * @param name 描述共享内存文件名称
         * @param length 用于指定创建多大的共享内存对象
         * @return MemoryFile 描述共享内存对象
         */
        public static MemoryFile createMemoryFile(String name,int length){
            try {
                return new MemoryFile(name,length);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static MemoryFile openMemoryFile(ParcelFileDescriptor pfd,int length,int mode){
            if(pfd == null){
                throw new IllegalArgumentException("ParcelFileDescriptor 不能为空");
            }
            FileDescriptor fd = pfd.getFileDescriptor();
            return openMemoryFile(fd,length,mode);
        }
    
        /**
         * 打开共享内存,一般是一个地方创建了一块共享内存
         * 另一个地方持有描述这块共享内存的文件描述符,调用
         * 此方法即可获得一个描述那块共享内存的MemoryFile
         * 对象
         * @param fd 文件描述
         * @param length 共享内存的大小
         * @param mode PROT_READ = 0x1只读方式打开,
         *             PROT_WRITE = 0x2可写方式打开,
         *             PROT_WRITE|PROT_READ可读可写方式打开
         * @return MemoryFile
         */
        public static MemoryFile openMemoryFile(FileDescriptor fd,int length,int mode){
            MemoryFile memoryFile = null;
            try {
                memoryFile = new MemoryFile("tem",1);
                memoryFile.close();
                Class<?> c = MemoryFile.class;
                Method native_mmap = null;
                Method[] ms = c.getDeclaredMethods();
                for(int i = 0;ms != null&&i<ms.length;i++){
                    if(ms[i].getName().equals("native_mmap")){
                        native_mmap = ms[i];
                    }
                }
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mFD", fd);
                ReflectUtil.setField("android.os.MemoryFile",memoryFile,"mLength",length);
                long address = (long) ReflectUtil.invokeMethod( null, native_mmap, fd, length, mode);
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return memoryFile;
        }
    
        /**
         * 获取memoryFile的ParcelFileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return ParcelFileDescriptor
         */
        public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            ParcelFileDescriptor pfd;
            FileDescriptor fd = getFileDescriptor(memoryFile);
            pfd = (ParcelFileDescriptor) ReflectUtil.getInstance("android.os.ParcelFileDescriptor",fd);
            return pfd;
        }
    
        /**
         * 获取memoryFile的FileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return 这块共享内存对应的文件描述符
         */
        public static FileDescriptor getFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            FileDescriptor fd;
            fd = (FileDescriptor) ReflectUtil.invoke("android.os.MemoryFile",memoryFile,"getFileDescriptor");
            return fd;
        }
    }

    ReflectUtil.java

    package wzr.com.slidefinish.util;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 反射工具类
     * Created by wuzr on 2016/6/27.
     */
    public class ReflectUtil {
    
        /**
         *根据类名,参数实例化对象
         * @param className 类的路径全名
         * @param params 构造函数需要的参数
         * @return 返回T类型的一个对象
         */
        public static Object getInstance(String className,Object ... params){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                if(params != null){
                    int plength = params.length;
                    Class[] paramsTypes = new Class[plength];
                    for (int i = 0; i < plength; i++) {
                        paramsTypes[i] = params[i].getClass();
                    }
                    Constructor constructor = c.getDeclaredConstructor(paramsTypes);
                    constructor.setAccessible(true);
                    return constructor.newInstance(params);
                }
                Constructor constructor = c.getDeclaredConstructor();
                constructor.setAccessible(true);
                return constructor.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 执行instance的方法
         * @param className 类的全名
         * @param instance 对应的对象,为null时执行类的静态方法
         * @param methodName 方法名称
         * @param params 参数
         */
        public static Object invoke(String className,Object instance,String methodName,Object ... params){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(methodName == null || methodName.equals("")){
                throw new IllegalArgumentException("methodName不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                if(params != null){
                    int plength = params.length;
                    Class[] paramsTypes = new Class[plength];
                    for(int i = 0;i < plength;i++){
                        paramsTypes[i] = params[i].getClass();
                    }
                    Method method = c.getDeclaredMethod(methodName, paramsTypes);
                    method.setAccessible(true);
                    return method.invoke(instance, params);
                }
                Method method = c.getDeclaredMethod(methodName);
                method.setAccessible(true);
                return method.invoke(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 执行指定的对方法
         * @param instance 需要执行该方法的对象,为空时,执行静态方法
         * @param m 需要执行的方法对象
         * @param params 方法对应的参数
         * @return 方法m执行的返回值
         */
        public static Object invokeMethod(Object instance,Method m,Object ... params){
            if(m == null){
                throw new IllegalArgumentException("method 不能为空");
            }
            m.setAccessible(true);
            try {
                return m.invoke(instance,params);
            } catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 取得属性值
         * @param className 类的全名
         * @param fieldName 属性名
         * @param instance 对应的对象,为null时取静态变量
         * @return 属性对应的值
         */
        public static Object getField(String className,Object instance,String fieldName){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(fieldName == null || fieldName.equals("")){
                throw new IllegalArgumentException("fieldName 不能为空");
            }
            try {
                Class c = Class.forName(className);
                Field field = c.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 设置属性
         * @param className 类的全名
         * @param fieldName 属性名
         * @param instance 对应的对象,为null时改变的是静态变量
         * @param value 值
         */
        public static void setField(String className,Object instance,String fieldName,Object value){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(fieldName == null || fieldName.equals("")){
                throw new IllegalArgumentException("fieldName 不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                Field field = c.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(instance, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 根据方法名,类名,参数获取方法
         * @param className 类名,全名称
         * @param methodName 方法名
         * @param paramsType 参数类型列表
         * @return 方法对象
         */
        public static Method getMethod(String className,String methodName,Class ... paramsType){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(methodName == null || methodName.equals("")){
                throw new IllegalArgumentException("methodName不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                return c.getDeclaredMethod(methodName,paramsType);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    源码地址: https://github.com/mingfeng002/TestMemFile

    文章来源:https://www.cnblogs.com/mingfeng002/p/9382601.html

     

     

    展开全文
  • 共享内存高效的IPC方式。 一、创建共享内存: #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); 例如: int shmid = shmget(key, size, 06...

    共享内存是高效的IPC方式。

    一、创建共享内存:

           #include <sys/ipc.h>
           #include <sys/shm.h>
    
           int shmget(key_t key, size_t size, int shmflg);
    
           例如:
           int shmid = shmget(key, size, 0660 | IPC_CREAT);
    

    参数说明:
    key: 若取值为IPC_PRIVATE,则创建的共享内存是进程私有的;
    若取值不为IPC_PRIVATE,则分以下两种情况:
    已经有共享内存关联到该key,则会返回该共享内存的标识符(shmid);
    该key尚未被使用,则会创建新的共享内存段,且段的长度与参数size相关。
    size: 作为共享内存段的大小。(man shmget说,size会被自动向上取整为PAGE_SIZE的整数倍;这点不敢苟同,因为实际通过ipcs -m查看共享内存段大小依然是size,并且看内核的newseg()函数也表明共享内存的大小是size。猜想可能是指实际占用的内存大小是PAGE_SIZE的整数倍,但共享内存在进程中的可用大小的的确确就是size。但是!!!用shmat将共享内存关联到进程地址空间时,返回的地址的确是PAGE_SIZE对齐的)。
    flag: 0660是指共享内存段的权限;IPC_CREAT指创建新的共享内存段

    更多信息见 man shmget

    ========
    二、将共享内存附加到本进程的地址空间

           #include <sys/types.h>
           #include <sys/shm.h>
    
           void *shmat(int shmid, const void *shmaddr, int shmflg);
    
           例如:
           void *attaddr = shmat(shmid, NULL, 0);
    

    ========
    三、将共享内存从本进程地址空间中分离
    shmdt:
    将共享内存段从本进程的地址空间中分离。参数shmaddr是shmat的返回值。

           #include <sys/types.h>
           #include <sys/shm.h>
    
           int shmdt(const void *shmaddr);
    
           例如:
           void *attaddr = shmdt(shmaddr);
    

    特别注意:该操作会使nattch递减。

    =======
    四、将共享内存置为待销毁状态
    shmctl(shmid, IPC_RMID, NULL)
    作用:将shmid标识的共享内存的状态置为dest.
    dest的作用是,当共享内存段的nattch为0时,系统会销毁该共享内存。
    dest还有一个作用是,被置为dest的共享内存段会释放它对key的占用,该共享内存段的key被置为0(但依旧保留对shmid的占用)。也就是说,dest状态的共享内存和它原来的key完全脱离了关系。如果之后再使用shmget和那个key,会创建一块新的共享内存,而找不到已dest的共享内存(即使dest共享内存的nattch不为0,尚未被系统真正销毁)。

    =======
    五、获取shmid标识的共享内存的状态信息。
    struct shmid_ds ds = {0};
    shmctl(shmid, IPC_STAT, &ds);

    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 */
    };
    

    重点
    1、执行shmget后在系统中用命令"ipcs -m"可以查看共享内存,但该段共享内存的 nattch 不会增加。只有在shmat后 nattch 才会递增。

    2、shmid是共享内存在系统中的唯一标识符。不同进程对同一key多次执行shmget,只要第二次及以后没有创建新的共享内存(也就是说在前后两次shmget中间没有使用shmctl-shmid-IPC_RMID将共享内存置为dest状态),那么shmget返回的shmid就是相同的。

    3、对同一key多次执行shmget-IPC_CREAT,第一次设置的size(rounded up to PAGE_SIZE)会在对齐PAGE_SIZE后成为共享内存段的实际大小;之后指定的size如果小于共享内存段的实际大小,那么shmget会正常返回相同的shmid,且共享内存的大小不变;若之后指定的size超过共享内存段的实际大小,shmget返回-1,errno报错"Invalid argument".

    4、同一进程对同一shmid多次执行shmat,shmat返回的地址是不同的。即同一共享内存段可以被分配到一个进程的地址空间中的多个地址。 并且,这一操作还会导致 nattch 增加。

    5、进程退出时,共享内存段的nattch会自动减少(进程对该段共享内存shmat了几次,nattch就会递增几次;进程退出时,nattch就会自动递减相应次数)。

    6、dest状态的共享内存会在nattch为0时自动销毁。dest状态的共享内存不再关联创建它时使用的key。

    7、非dest状态的共享内存即使nattch为0,也依然会被系统保留。

    ==============
    下面用文字叙述一个例子,可以将上述重点串联起来。

    步骤1:进程A使用shmget(key=0x11223344,size = 123, flag = 0660 | IPC_CREAT),在系统中创建了一块共享内存,shmget()返回的shmid为23456。
    //执行后,shell输入"ipcs -m"命令可以看到,系统中新增加了一块共享内存
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 0 空

    步骤2:进程A使用shmat(shmid=23456),将共享内存关联到本进程的地址空间。shmat()返回的地址为 0x7f36f9df5000
    //执行后,shell输入"ipcs -m"命令可以看到,nattch = 1,其他不变
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 1 空

    步骤3:进程B(或者依然是进程A),继续使用步骤1中的参数,只把size减小为100.
    //执行后,shell输入"ipcs -m"命令可以看到,共享内存的size依然是123,其他属性也没有改变。
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 1 空

    步骤4:进程X (X可以是A,B,C…),继续使用步骤1中的参数,只是把size设置为300. 该步骤会执行失败,shmget()返回-1
    //执行后,shell输入"ipcs -m"命令可以看到,没有发生任何变化。
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 1 空

    步骤5:进程A使用shmat(shmid=23456)将共享内存关联到本进程的地址空间。shmat()返回的地址为 0x7f36f9df4000(注意,在步骤2中返回的地址为0x7f36f9df5000,也就是说,虽然共享内存的size只有123byte,但在进程地址空间中占用的大小必须对齐到PAGE_SIZE的整数倍;同时也说明了一块共享内存可以在一个进程中多次关联,只是返回的关联地址不同)
    //执行后,nattch加1,nattch=2
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 2 空

    步骤6:进程A 执行一次shmdt(0x7f36f9df4000)。
    //执行后,nattch减1,nattch=1
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 1 空

    步骤7:进程A直接退出(注意,没有执行shmdt(0x7f36f9df5000))
    //退出后,nattch直接变成了0。也就是说,进程退出时会自动递减共享内存的nattch。
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 0 空

    步骤8:进程B执行 shmat(shmid=23456)
    //执行后,nattch加1,nattch=1
    //key shmid owner perms bytes nattch status
    //0x11223344 23456 user_12 660 123 1 空

    然后执行shmctl(23456, IPC_RMID, NULL)
    //执行后,key变为0x00000000,status变为dest
    //key shmid owner perms bytes nattch status
    //0x00000000 23456 user_12 660 123 1 dest

    步骤9:进程B,继续使用步骤1中的参数,只是把size设置为300.
    //执行后,shell输入"ipcs -m"命令可以看到。一块新的共享内存出现了。shmid=56789,size=300,nattch=0
    //key shmid owner perms bytes nattch status
    //0x00000000 23456 user_12 660 123 1 dest
    //0x11223344 56789 user_12 660 300 0 空

    步骤10:进程B直接退出(注意,进程B没有执行shmdt())
    //B退出后,23456的nattch直接变成了0。然后由于23456共享内存已经处于dest状态,所以系统直接将它销毁了。
    //执行后,shell输入"ipcs -m"命令可以看到,系统中已经没有shmid为23456的共享内存了。
    //56789依然存在。也就是说,即使创建共享内存的进程已经终止运行,但只要该共享内存没有被置为dest,那么它就会继续存在于系统中,并且之后可以被其他进程获取和关联。
    //key shmid owner perms bytes nattch status
    //0x11223344 56789 user_12 660 300 0 空

    最后附上代码。(代码只是简单示例,并不能完整体现上述特性)

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    #define DO_IF(exp, how) \
    	if (exp) { \
    		how; \
    	}
    
    #define TEST_SHM_KEY 0x11223344
    int g_shmid = 0;
    
    void print_shm_ds(int shmid)
    {
    	struct shmid_ds ds = {0};
    
    	shmctl(shmid, IPC_STAT, &ds);
    
    	printf("\nds.shm_nattch\tds.shm_segsz\n%u\t%u\n", (unsigned) ds.shm_nattch, (unsigned) ds.shm_segsz);
    }
    
    void* get_shm(key_t key ,size_t size)
    {
    	int shmid = 0;
    	void *attaddr = NULL;
    
    	shmid = shmget(key, size, 0666 | IPC_CREAT);
    	DO_IF(-1 == shmid, perror(0);return -1);
    	g_shmid = shmid;
    
    	attaddr = shmat(shmid, NULL, 0);
    	DO_IF(-1 == (int)attaddr, perror(0);return -1);
    
    	printf("shmid[%#x] attaddr[%p]", shmid, attaddr);
    	print_shm_ds(shmid);
    
    	return attaddr;
    }
    
    int detach_shm(void *addr)
    {
    	return shmdt(addr);
    }
    
    int release_shm(int shmid)
    {
    	PT("");
    	if (-1 == shmctl(shmid, IPC_RMID, NULL))
    	{
    		perror("shmctl return -1");
    		return -1;
    	}
    
    	return 0;
    }
    
    int main()
    {
    	int shmid = 0;
    	void *a,*b,*c;
    
    	printf("private shm id %d\n", shmid = shmget(IPC_PRIVATE, 256, 0660 | IPC_CREAT));
    	shmat(shmid, NULL, 0);
    
    	printf("this is main\n");
    
    	a = get_shm(TEST_SHM_KEY, 228);
    	sleep(1);
    
    	b = get_shm(TEST_SHM_KEY, 5164);
    	sleep(1);
    
    	c = get_shm(TEST_SHM_KEY, 32);
    	sleep(1);
    
    	release_shm(g_shmid);
    	sleep(5);
    
    	return 0;
    }
    
    展开全文
  • 文章目录前言原理初探共享内存的创建过程系列 API 的使用ftokshmgetshmatshmdtshmctl共享内存实例 前言 总结一下最近对于共享内存的学习, 可能比较浅显或者有疏漏, 欢迎指正! 原理初探 我们知道, 进程空间相互隔离,...


    前言

    总结一下最近对于共享内存的学习, 可能比较浅显或者有疏漏, 欢迎指正!

    原理初探

    我们知道, 进程空间相互隔离, 互相对立, 但是共享内存允许多个进程可以访问同一块内存来达到进程间通信的目的.

    共享内存是最高效的 IPC 机制, 它不涉及任何进程间的数据传输, 而且他和进程同处于用户空间, 不像消息队列, 信号量是内核空间的系统对象, 不需要花费额外的数据拷贝, 但是同时他并没有预防竞态条件, 也就是说在多进程利用共享内存进行通信的情况下, 我们需要自己去利用锁等操作来进行同步

    共享内存的创建过程

    当我们创建了一块共享内存, 其实是在 tmpfs 中创建了一个文件 (这个文件是存储于内存的), 也就意味着在 tmpfs 中创建了一个 iNode 节点

    然后我们需要将这个创建好的文件映射到进程中 (如下图, 此图来自网络, 应该是哪个博客或者知乎吧…已经记不清了)

    在这里插入图片描述

    我们创建的这块共享内存不会随着进程的结束而被释放, 他会在关机前一直存在于内存中, 除非被进程明确的删除

    也可以使用 `ipcrm + 共享内存 id ` 命令删除
    

    在这里插入图片描述

    比如这里我在刚开始练习的时候没有删除创建的共享内存
    

    在这里插入图片描述

    系列 API 的使用

    ftok

    #include <sys/shm.h>
    #include <sys/types.h>
    key_t ftok ( const char* fname, int fd);
    
    成功返回一个key_t值, 失败返回-1
    

    fname 参数是一个必须存在且能访问到的目录
    id 子序号, 自己约定, 只有8个比特位被指用

    注意, 当文件路径和子序号都相同时返回的并不一定永远返回一样的key值, 如果该路径指向的文件或者目录被删除而又重新创建, 就算名字还是一样, 但是文件系统会赋予它不同的 iNode 信息, 所以返回的 key 值就不同了

    shmget

    #include <sys/shm.h>
    int shmget (key_t key, size_t size, int shmflag);
    
    用来创建一块共享内存并返回其 id 
    或者获得一块已经被创建的共享内存的 id
    

    成功返回一个正整数值, 这个整数是共享内存的标识符, 失败时返回 -1, 错误存储于 errno

    key_t key 参数用来唯一标识一段全局共享内存, 通常通过 ftok 函数获得,
    size_t size 参数是创建的共享内存的大小, 单位为字节, 如果是要创建一块共享内存, 此参数必须被指定, 如果是要获取一块创建好的共享内存的 id, 可以将其设置为 0
    int shmglag 参数为 0 为获取共享内存 id, 为 IPC_CREAT 时是创建一个新的共享内存, 通常要同时指定权限 (和权限进行 | 运算)
    同时还能取IPC_EXCL , 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。

    使用 shmget 创建的共享内存段会全部被初始化为 0, 同时和他关联的内核数据结构 shmid_ds 将被创建和初始化

    但是我尝试发现并没有被初始化为0, 请谨慎使用

    struct shmid_ds { 
        struct ipc_perm    shm_perm;      /* 操作权限 */ 
        size_t             shm_segsz;     /* 大小,单位是字节 */ 
        __kernel_time_t    shm_atime;     /* 对这段内存最后一次调用shmat的时间 */ 
        __kernel_time_t    shm_dtime;     /* 最后一次调用shmdt的时间*/ 
        __kernel_time_t    shm_ctime;     /* 最后一次调用shmctl的时间*/ 
        __kernel_ipc_pid_t shm_cpid;      /* 创建者的pid*/ 
        __kernel_ipc_pid_t shm_lpid;      /* 最后一次执行shmat或shmdt的进程的pid*/ 
        unsigned short     shm_nattch;    /*目前关联到次共享内存的进程的数量*/ 
        unsigned short     shm_unused;    /* 以下为填充 */ 
        void               *shm_unused2; /* ditto - used by DIPC */ 
        void               *shm_unused3; /* unused */ 
    };
    

    shmat

    前面我们也说道, 我们创建共享内存其实是创建了一个文件, 然后要把它映射到当前进程, 即, 我们在使用 shmget 创建了一块共享内存之后, 要使用 shmat 将他关联到当前进程, 同样的使用完不再需要之后, 也需要使用 shmdt 将其分离, 让我们先看 shmat

    #include <sys/shm.h>
    void* shmat ( int shm_id, const void* shm_addr, int shmflag );
    
    成功返回映射到进程的地址空间 失败返回 (void *)-1并将错误存储于 errno
    

    shm_id 参数是 shmget 返回的共享内存 id,
    shm_addr 参数是指定共享内存在进程内存地址的映射位置, 推荐使用 NULL, 由内核自己决定
    shmflag一般为0

    shmat 调用成功后, 会修改 shmid_ds 的部分字段:
    将 shm_nattach 加一
    将 shm_lpid 设置为调用进程 pid
    将 shm_atime 设置为当前时间

    shmdt

    在共享内存使用完之后使用此函数将其从进程地址空间分离

    #include <sys/shm.h>
    int shmdt ( const void* shm_addr );
    
    成功返回 0, 失败返回 -1
    

    shm_addr 参数是共享内存在进程的映射地址, 即 shmat 返回的值

    调用 shmdt 并不会删除共享内存
    调用成功时修改内核数据结构 shmid_ds 部分字段:
    将 shm_nattach 减一
    将 shm_lpid 设置为调用进程的pid
    将 shm_dtime 设置为当前时间

    shmctl

    管理共享内存

    #include 
    int shmctl ( int shm_id, int command, struct shmid_ds* buf );
    
    失败返回-1, 并存储错误于 errno, 成功时的返回值取决于 command
    

    shm_id 是共享内存标识符
    command 指定要执行的命令

    常用命令为 IPC_RMID, 即, 删除共享内存

    如果共享内存已经与所有访问它的进程断开了连接,则调用IPC_RMID子命令后,系统将立即删除共享内存的标识符,并删除该共享内存区,以及所有相关的数据结构;

    如果仍有别的进程与该共享内存保持连接,则调用IPC_RMID子命令后,该共享内存并不会被立即从系统中删除,而是被设置为IPC_PRIVATE状态,并被标记为"已被删除";直到已有连接全部断开,该共享内存才会最终从系统中消失。

    共享内存实例

    通过共享内存进行同机间的进程间通信

    创建一个长度为10的 Stu 数组, 向其中写入数据, 另一个进程读取
    代码很简单, 就不做注释了
    
    /*
    write.cpp
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    typedef struct Stu 
    {
        int age;
        char name[10];
    } Stu;
    
    int main() 
    {
     
        int id = shmget((key_t)1234, sizeof(Stu) * 10, IPC_CREAT | 0644);
        if (id == -1) 
        {
            perror("shmget "), exit(1);
        }
        
        Stu* t = (Stu *)shmat(id, NULL, 0);
        for (int i = 0; i < 5; i++) 		//初始化前五个
        {
            (t + i)->age = i;
            strcpy((t + i)->name, "lvbai");
        }
    
        shmdt(t);
     
        return 0;
    }
    
    
    /*
    read.cpp
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    typedef struct Stu 
    {
        int age;
        char name[10];
    } Stu;
    
    int main() 
    {
        int id = shmget((key_t)1234, 0, IPC_CREAT);
        if (id == -1) 
        {
            perror("shmget "), exit(1);
        }
    
        void* p = NULL;
        p = shmat(id, NULL, 0);
        if (p == (void *)-1) 
        {
            perror("****** : ");
        }
    
    
        Stu* ptr = (Stu *)p;
    
    	printf("here is message : \n");
    
        for (int i = 0; i < 10; i++) 
        {
            printf("age = %d, name = %s\n", (ptr + i)->age, (ptr + i)->name);
        }
    
        shmdt(p);
    
        shmctl(id, IPC_RMID, 0);
       
       
        
        return 0;
    }
    

    在这里插入图片描述

    展开全文
  • 前面的博文介绍了Linux间进程通信方式实际上效率不是很高,假如在两个进程中进行快速、大量数据传输的时候,使用管道、消息队列这样的通信方式就不合适了,这里介绍到另外一种高效的进程间通信方式:共享内存。...
  • 使用场景监控汇总目前正在用的一个场景,针对某一台机器上的错误进行汇总并报警,我们把一分钟之内的相同报警合并成一条,用共享内存来暂存,非常实用且高效。PHP SESSION如果你是单机的服务,且又启用了session,...
  • Linux 共享内存

    2021-03-20 15:11:14
      共享内存(Shared Memory)就是 允许多个进程访问同一个内存空 间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的...
  • linux 共享内存

    2018-09-14 00:34:00
    所以,共享内存通常和其他进程间通信方式一起使用。 linux 共享内存有四个系统调用:shmget, shmat, shmdt, shmctl shmget 创建一段新的共享内存,或者获取一段已经存在的共享内存: ...
  • 共享内存

    2016-04-17 13:14:28
    共享内存---- 数据传递 --- 最高效 ,但 不提供 同步和互斥 --- 和 信号量 搭配使用头文件:#include<sys/ipc.h>#include<sys/shm.h> attch(挂接) nattach(有n个进程挂接到共享内存上) ... ...void *...
  • 共享内存是一种最为高效的进程间通讯方式,进程可以直接读写内存,不需要任何数据的复制。 共享内存的实现主要分为俩个步骤1-创建共享内存 shmget(); 2-映射共享内存shmat(); 当然还有撤销操作shmdt(); 为了使创建的...
  • 在linux系统开发当中,时常需要在多个进程之间交换数据,在多个进程之间交换数据,有很多方法,但最高效的方法莫过于共享内存。  linux共享内存是通过tmpfs这个文件系统来实现的,tmpfs文件系的目录为/dev/shm,/...
  • 安卓共享内存

    2020-05-19 19:34:36
    Ashmem机制使用linux的mmap系统调用,可以将同一段物理内存映射到不同进程各自的虚拟地址空间,从而实现高效的进程间共享。 在linux上“一切皆文件”,一块共享内存当然也不例外。因此,在用户态,我们能看到的重要...
  • Android应用层使用共享内存机制进行进程间交换数据Android提供了一个高效共享内存机制。如果应用中涉及到在多个进程间交换数据时使用Android提高的共享内存机制将会大大的提高效率。但是也许是出于安全考虑,在...
  • 基于windows下用共享内存作为桥梁,使用数组实现的环形队列缓冲区,用进程互斥锁、事件锁、读写锁相结合实现的高效跨进程通信方案,实测16个进程同时写入缓冲区,读取缓冲区进程的响应速度最慢3ms,平均1ms。...
  • 整理linux共享内存

    2020-06-03 10:33:14
    共享内存就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程...
  • 共享内存例子

    2013-10-22 13:48:04
    共享内存是LUNIX 系统中最底层的通信机制,也是最快速的通信机制。共享内存通过两个或多个进程共享同一块内存区域来实现进程间的通信。...但实际的问题在于,当两个或多个进程使用共享内存进行通信时,同步问题
  • 19.90 积分1共享内存共享内存+互斥量实现互斥量实现 linux 进程间通信进程间通信一、一、共享内存简介共享内存简介 共享内存是进程间通信中高效方便的方式之一。共享内存允许两个或更多进程访问同 一块内存,就如同 ...
  • System V IPC之共享内存

    2019-10-02 14:36:44
    共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存, 而不需要任何数据的拷贝 共享内存在内核空间创建, 可以被进程映射到用户空间访问 由于多个进程可同时访问共享内存 , 因此需要同步和互斥...
  • 共享内存,消息队列

    2019-07-23 16:31:53
    共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题第,我们必须用其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。因此,共享内存通常和其他进程间通信方式一起...
  • MemoryFile匿名共享内存

    2018-07-28 17:41:00
    Android提供了一个高效共享内存机制。如果应用中涉及到在多个进程间交换数据时使用Android提高的共享内存机制将会大大的提高效率。但是也许是出于安全考虑,在应用层使用共享内存机制将会遇到很多障碍,这篇文章...
  • 共享内存解读

    2014-03-13 11:59:00
    来源:最初用读写同一个文件的方式实现管道、消息队列的机制,后来加以改进,将文件映射到内存来实现,完成高效的通信机制(这里的文件和共享内存区是“同步的”,即一致的,同时也不会是时时刻刻的一致,内核会完成...
  •  共享内存是最高效的IPC机制,因为它不涉及进程之间任何的数据传输。这种高效率带来的问题是,我们必须用其他辅助手段来同步进程对共享内存的访问,否则就会产生竞态条件。因此,共享内存通过和其他进程间通信方式...
  • PAGE / NUMPAGES 共享内存+互斥量实现linux进程间通信 共享内存简介 共享内存是进程间通信中高效方便的方式之一共享内存允许两个或更多进程访问同一块内存就如同 malloc) 函数向不同进程返回了指向同一个物理内存...
  • 此前发表过一篇关于使用信号量做php进程同步的例子:http://lajabs.net/?p=159,其...遂考虑使用共享内存方式实现一个属于php的“自旋锁”,主要特点是:1、检测和避免死锁2、并可以自定义锁定超时3、可以在运行结束...
  • 匿名共享内存(Ashmem):Ashmem是一种共享内存的机制,它利用了Linux的mmap系统调用,将不同进程中的同一段物理内存映射到进程各自的虚拟地址空间,从而实现高效的进程间共享。它以驱动程序的形式实现在内核空间。...
  • 共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题是,我们必须用其他辅助手段来同步进程对共享内存使用,否则会产生竞态条件。Linux共享内存的API都定义在sys/shm.h头文件中...
  • 一、共享内存简介 ... 共享内存并未提供进程同步机制,使用共享内存完成进程间通信时,需要借助互斥量或者信号量来完成进程的同步。这里说一下互斥量与信号量的区别。互斥量用于线程的互斥,信号量用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 731
精华内容 292
关键字:

共享内存高效使用