Linux 进程通讯 - 共享内存机制

Linux进程间共享内存通信时如何同步?(附源码) 然后,程序使用互斥量和条件变量来同步进程之间的访问共享内存区域。需要注意的是,在使用条件变量时,我们需要遵循一些规则来保证程序的正确性,如在等待条件变量时必须锁定互斥量,并使用while循环来检查条件变量的值是否满足要求,等待条件变量信号的线程必须在等待之前锁定互斥量,在等待之后解锁互斥量,等待条件变量信号的线程可能会因为接收到信号而提前返回等等。总之,使用互斥量和条件变量来实现进程间共享内存通信的同步,需要我们仔细考虑程序中所有可能出现的情况,并正确地使用互斥量和条件变量函数来同步进程之间的访问。 阅读详情

共享内存机制,就是在物理内存中划分一段内存,多个进程间可以共同访问这段内存,也可对这段内存做修改操作,从而达到进程通讯的效果!

共享内存机制是允许两个或多个进程(不相关或有亲缘关系)访问同一个逻辑内存的机制。它是共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。

两种常用共享内存方式

  • System V版本的共享内存 shm

        1. 多个进程直接共享内存

  • 文件映射 mmap        

        1. 文件进行频繁读写,将一个普通文件映射到内存中

        2. 将特殊文件进行匿名内存映射,为关联进程提供共享内存空间

        3. 为无关联的进程提供共享内存空间,将一个普通文件映射到内存中


目录

一、System V版本

1. ftok

2. shmid

3. shmat

4. shmdt

5. shmctl

6. 示例

1). 例一

2). 例二

二、mmap文件映射

1. mmap

2. munmap

3. msync

4. mremap

5. 示例

1). 例一

2). 例二

3). 例三,实际意义的项目用法

三、总结


一、System V版本

原理:利用共享内存完成进程通讯,两个进程都通过虚拟地址空间到用户页表,然后通过用户级页表映射到物理内存的相同一块内存区域。

1. ftok

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

key_t ftok(const char *pathname, int proj_id);

描述:生成key标识符返回;

参数:

        pathname

                文件路径名;

        proj_id

                id是子序号;虽然是int类型,但是只使用8bits(1-255);

返回值:

        成功:返回key标识符;

        失败:返回-1,并设置错误标志errno;

例:

key_t key = ftok("a.txt", 1);
if ((key_t)-1 == key) {
    fprintf(stderr, "ftok failed. reason: %s\n", strerror(errno));
    exit(1);
}

2. shmid

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

int shmget(key_t key, size_t size, int shmflg);

描述:创建一个共享内存块,返回这个共享内存块的标识符shmid;

参数:

        key

                共享内存的key,可随机取;但互相通信的进程需一致;

        size

                申请的共享内存的大小,为4k(4096)的整数倍;

        shmflg

                IPC_CREAT,创建新的共享内存,存在则返回共享内存标识符;

                IPC_EXCL,不存在就创建,存在则报错;

返回值:

        成功:返回标识符;

        失败:返回-1,并设置错误标志errno;

例:

int shmid = shmget((key_t)1234, 4096, 0666|IPC_CREAT);
if (-1 == shmid) {
    fprintf(stderr, "shmget failed!\n");
    exit(1);
}

3. shmat

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

void *shmat(int shmid, const void *shmaddr, int shmflg);

描述:挂接共享内存(将进程地址空间挂接到物理空间,可以有多个挂接);

参数:

        shmid

                挂接的共享内存id;

        shmatddr

                一般为0,表示连接到由内核选择的第一个可用地址上;

        shmflg

                一般为0;

返回值:

        成功:返回共享内存段的地址;

        失败:返回(void *)-1,并设置错误标志errno;

例:

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

4. shmdt

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

int shmdt(const void *shmaddr);

描述:取消共享内存映射;

参数:

        shmaddr

                共享内存段的地址;

返回值:

        成功:返回0;

        失败:返回-1,并设置错误标志errno;

例:

int ret = shmdt(shm);
if (-1 == ret) {
    fprintf(stderr, "shmdt failed!\n");
    exit(1);
}

5. shmctl

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

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

描述:控制共享内存,可用于删除内存共享等操作;

参数:

        shmid

                由shmget返回的共享内存标识码;

        cmd

                将要采取的动作(可取值:IPC_STAT、IPC_SET、IPC_RMID);

        buf

                指向一个保存着共享内存的模式状态和访问权限的数据结构;   

返回值:

        成功:IPC_INFO、SHM_INFO、SHM_STAT 返回其他一些东东;其他操作返回0;

        失败:返回-1,并设置错误标志errno;

例:

int ret = shmctl(shmid, IPC_RMID, 0);
if (-1 == ret) {
    fprintf(stderr, "shmctl(IPC_RMID) failed, reason:%s\n", strerror(errno));
    exit(1);
}

6. 示例

1). 例一

shmwrite.cpp

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>


typedef struct MSG {
    char name[32];
    int age;
    char sex[32];
}Msg;

int main(int argc, char **argv) {
    
    void *shm = NULL;    
    int shmid = 0;                   // 共享内存标识符
    const key_t key = (key_t)2023;   // 共享内存的key
    
    Msg msg;
    sprintf(msg.name, "%s", "老王");
    msg.age = 25;
    sprintf(msg.sex, "%s", "男");
    
    // 创建共享内存
    shmid = shmget(key, sizeof(Msg), 0666|IPC_CREAT);
    if (-1 == shmid) {
        fprintf(stderr, "shmget failed!\n");
        exit(1);
    }
    
    // 将共享内存连接到当前进程的地址空间
    shm = shmat(shmid, (void*)0, 0);
    if ((void*)-1 == shm) {
        fprintf(stderr, "shmat failed!\n");
        exit(2);
    }
    
    printf("Memory attached at %p \n", shm);
    
    // 设置共享内存
    Msg *p = (Msg*)shm;
    memcpy(p, &msg, sizeof(Msg));
    
    
    sleep(3);
    
    printf("name = %s\n", p->name);
    printf("age = %d\n", p->age);
    printf("sex = %s\n", p->sex);
    
    
    // 把共享内存从当前进程中分离
    /*int ret = shmdt(shm);
    if (-1 == ret) {
        fprintf(stderr, "shmdt failed!\n");
        exit(3);
    }*/
    
    
    return 0;
}

shmread.cpp

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <errno.h>


typedef struct MSG {
    char name[32];
    int age;
    char sex[32];
}Msg;

int main(int argc, char **argv) {
    
    void *shm = NULL;    
    int shmid = 0;                   // 共享内存标识符
    const key_t key = (key_t)2023;   // 共享内存的key
    
    Msg msg;    // 指向shm
    
    
    // 创建共享内存
    shmid = shmget(key, sizeof(Msg), 0666|IPC_CREAT);
    if (-1 == shmid) {
        fprintf(stderr, "shmget failed!\n");
        exit(1);
    }
    
    // 将共享内存连接到当前进程的地址空间
    shm = shmat(shmid, (void*)0, 0);
    if ((void*)-1 == shm) {
        fprintf(stderr, "shmat failed!\n");
        exit(2);
    }
    
    printf("Memory attached at %p \n", shm);
    
    // 设置共享内存
    memcpy(&msg, shm, sizeof(Msg));
    
    printf("name = %s\n", msg.name);
    printf("age = %d\n", msg.age);
    printf("sex = %s\n", msg.sex);
    

    sprintf(msg.name, "%s", "小红");
    msg.age = 24;
    sprintf(msg.sex, "%s", "女");
    
    memcpy(shm, &msg, sizeof(Msg));
    

    
    // 把共享内存从当前进程中分离
    int ret = shmdt(shm);
    if (-1 == ret) {
        fprintf(stderr, "shmdt failed!\n");
        exit(3);
    }
    
    
    // 删除共享内存
    ret = shmctl(shmid, IPC_RMID, 0);
    if (-1 == ret) {
        fprintf(stderr, "shmctl(IPC_RMID) failed, reason:%s\n", strerror(errno));
        exit(4);
    }
    
    
    return 0;
}

2). 例二

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>


int main(int argc, char **argv) {
    
    void *shm = NULL;    
    int shmid = 0;                   // 共享内存标识符
    const key_t key = (key_t)2023;   // 共享内存的key
    pid_t fpid;     // 进程唯一标识符
    
    char msg[1024] = { '\0' };
    
    // 创建共享内存
    shmid = shmget(key, sizeof(msg), 0666|IPC_CREAT|IPC_EXCL);
    if (-1 == shmid) {
        fprintf(stderr, "shmget failed!\n");
        exit(1);
    }
    
    // 将共享内存连接到当前进程的地址空间
    shm = shmat(shmid, (void*)0, 0);
    if ((void*)-1 == shm) {
        fprintf(stderr, "shmat failed!\n");
        exit(2);
    }
    
    printf("Memory attached at %p \n", shm);
    
    
    // 设置共享内存
    char *p = (char*)shm;
    memcpy(p, &msg, sizeof(shm));
    
    
    // 创建进程
    fpid = fork();
    if (fpid < 0) {
        printf("error in fork!\n");
        exit(3);
    
    } else if (0 == fpid) { // 子进程的操作
        printf("Child:p = %s\n", p);
        
        sprintf(p, "%s", "Hello World!");

        sleep(1);
    
    } else {    // 父进程的操作
        sprintf(p, "%s", "this is message!");   // 修改内存中的值
        
        sleep (1);
        
        printf("Parent:p = %s\n", p);
        
        wait(NULL);
    
        // 把共享内存从当前进程中分离
        int ret = shmdt(shm);
        if (-1 == ret) {
            fprintf(stderr, "shmdt failed!\n");
            exit(3);
        }
        
        
        // 删除共享内存
        ret = shmctl(shmid, IPC_RMID, 0);
        if (-1 == ret) {
            fprintf(stderr, "shmctl(IPC_RMID) failed, reason:%s\n", strerror(errno));
            exit(4);
        }
    }
      
    
    return 0;
}


二、mmap文件映射

原理:将一个文件或者其他对象映射进内存;

        1. 使用普通文件提供的内存映射;

        2. 使用特殊文件提供匿名内存映射。

1. mmap

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

描述:在调用的虚拟地址空间中创建一个新的映射;

参数:

        addr

                指向欲映射的内存起始地址,通常设为NULL,让系统自动选定地址,映射成功后返回该地址;

        length

                代表将文件中多大的部分映射到内存;(4k的整数倍)

        prot

                映射区域的保护方式,可以是以下几种反射光hi的组合:

                        PROT_EXEC        执行

                        PROT_READ        读取

                        PROT_WRITE        写入

                        PROT_NONE        不能存取

        flags

                影响映射区域的各种特性;必须要指定MAP_SHARED 或 MAP_PRIVATE;

                        MAP_SHARED        映射区域数据与文件对应,允许其他进程共享;

                        MAP_PRIVATE        映射区域生成文件的copy,修改不同步文件;

                        MAP_ANONYMOUS        建立匿名映射;此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享;

                        MAP_DENYWRITE        允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝;

                        MAP_LOCKED            将映射区域锁定住,这表示该区域不会被设置swap;

        fd

                要映射到内存中的文件描述符;如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设置为-1;有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero 文件,然后对该文件进行映射,可以同样达到匿名映射的效果;

        offset

                文件映射的偏移量,通常设置为0,代表从文件最前方开始映射,offset必须是分页大小的整数倍,即4k的整数倍;

返回值:

        成功:返回一个指向映射区域的指针;

        失败:返回MAP_FAILED(即(void *) -1),并设置错误标志errno;

例:

void *mem = mmap(NULL, FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 映射失败
if (MAP_FAILED == mem) {
    fprintf(stderr, "map file failed: %s", strerror(errno));      
    exit(1);
}

2. munmap

#include <sys/mman.h>

int munmap(void *addr, size_t length);

描述:解除映射;

参数:

        addr

                文件映射到进程空间的地址;

        length

                映射空间的大小;

返回值:

        成功:返回0;

        失败:返回-1,并设置错误标志errno;

例:

int ret = munmap(p, sizeof(p));
if(ret < 0) {
    perror("munmap error. reason:");
    exit(1);
}

3. msync

#include <sys/mman.h>

int msync(void *addr, size_t length, int flags);

描述:实现磁盘文件内容与共享内存区中的内容一致,即同步操作;

参数:

        addr

                文件映射到进程空间的地址;

        length

                映射空间的大小;

        flags

                MS_ASYNC        异步,调用会立即返回,不等更新完成;

                MS_SYNC           同步,调用后会等到更新完成之后才返回;

返回值:

        成功:返回0;

        失败:返回-1,并设置错误标志errno;

例:

int ret = msync(p, FILE_SIZE, MS_ASYNC);
if (MAP_FAILED == p) {
    fprintf(stderr, "msync failed. reason:%s\n", strerror(errno));
    exit(1);
}

4. mremap

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <sys/mman.h>

void *mremap(void *old_address, size_t old_size, size_t new_size, int flags);

描述:扩大(或缩小)现有的内存映射;

参数:

        old_address

                旧的文件映射到进程空间的地址;

        old_size

                旧的映射空间的大小;

        new_size

                重新映射指定的新空间大小;

        flags

                取值0或者MREMAP_MAYMOVE,0代表不允许内核移动映射区域,MREMAP_MAYMOVE则表示内核可以根据实际情况移动映射区域以找到一个符合new_size大小要求的内存区域;

返回值:

        成功:返回0;

        失败:返回MAP_FAILED即(void*)-1,并设置错误标志errno;

例:

p = (char*)mremap(p, FILE_SIZE, 2*FILE_SIZE, MREMAP_MAYMOVE);
if (MAP_FAILED == p) {
    fprintf(stderr, "mremap failed. reason:%s\n", strerror(errno));
    exit(1);
}

5. 示例

1). 例一

mmap_write.cpp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>

typedef struct MSG {
    char name[32];
    int age;
    char sex[32];
}Msg;


int main(int argc, char **argv) {
    if (2 > argc) {
        printf("usage: %s file.\n", argv[0]);
        exit(1);
    }
    
    Msg msg;
    sprintf(msg.name, "%s", "小明");
    msg.age = 25;
    sprintf(msg.sex, "%s", "男");
    
    // 打开一个文件
    int fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0664);
    if (fd < 0) {
        perror("open error. reason:");
        exit(2);
    }
    
    // 修改文件的大小
    ftruncate(fd, sizeof(Msg));
    
    // 将文件映射到内存
    void *mem = mmap(NULL, sizeof(Msg), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    // 映射失败
    if (MAP_FAILED == mem) {
        fprintf(stderr, "map file failed: %s", strerror(errno));      
        exit(3);
    }
    close(fd); //关闭不用的文件描述符
    Msg *p = (Msg*)mem;
    memcpy(p, &msg, sizeof(Msg));   // 将内容拷贝映射的内存
    
    
    sleep(3);
    
    printf("name = %s\n", p->name);
    printf("age = %d\n", p->age);
    printf("sex = %s\n", p->sex);
    
    
    // 结束映射
    int ret = munmap(p, sizeof(Msg));
	if(ret < 0)
	{
		perror("munmap error. reason:");
		exit(4);
	}

	return 0;

}

mmap_read.cpp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>

typedef struct MSG {
    char name[32];
    int age;
    char sex[32];
}Msg;


int main(int argc, char **argv) {
    if (2 > argc) {
        printf("usage: %s file.\n", argv[0]);
        exit(1);
    }
    
    Msg msg;

    
    int fd = open(argv[1], O_RDWR, 0664);
    if (fd < 0) {
        perror("open error. reason:");
        exit(2);
    }
    
    
    // 将文件映射到内存
    void *mem = mmap(NULL, sizeof(Msg), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    // 映射失败
    if (MAP_FAILED == mem) {
        fprintf(stderr, "map file failed: %s", strerror(errno));      
        exit(3);
    }
    close(fd); //关闭不用的文件描述符
    Msg *p = (Msg*)mem;
    memcpy(&msg, p, sizeof(Msg));   // 将内容拷贝映射的内存
    
    
    printf("name = %s\n", msg.name);
    printf("age = %d\n", msg.age);
    printf("sex = %s\n", msg.sex);
    
       
    sprintf(p->name, "%s", "小红");
    p->age = 24;
    sprintf(p->sex, "%s", "女");
    
    
    
    // 结束映射
    int ret = munmap(mem, sizeof(Msg));
	if(ret < 0)
	{
		perror("munmap error. reason:");
		exit(4);
	}

	return 0;

}

2). 例二

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>


#define FILE_SIZE   32

int main(int argc, char **argv) {
    
    void *mem = NULL;    
    pid_t fpid;     // 进程唯一标识符
    int ret = -1;
    
    if (2 > argc) {
        printf("usage: %s file.\n", argv[0]);
        exit(1);
    }
    
    // open file
    int fd = open(argv[1], O_RDWR|O_CREAT, 0664);
    if (fd < 0) {
        perror("open error. reason:");
        exit(2);
    }
    
    // 修改文件的大小
    ftruncate(fd, FILE_SIZE);
    
    // 将文件映射到内存
    mem = mmap(NULL, FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    // 映射失败
    if (MAP_FAILED == mem) {
        fprintf(stderr, "map file failed: %s", strerror(errno));      
        exit(3);
    }

    close(fd); //关闭不用的文件描述符
    char *p = (char*)mem;
    
    
    // 创建进程
    fpid = fork();
    if (fpid < 0) {
        printf("error in fork!\n");
        exit(3);
    
    } else if (0 == fpid) { // 子进程的操作
        printf("Child:p = %s, strlen(p) = %ld\n", p, strlen(p));
        
        // 扩大现有的内存映射
        p = (char*)mremap(p, strlen(p), 2*FILE_SIZE, MREMAP_MAYMOVE);
        if (MAP_FAILED == p) {
            fprintf(stderr, "mremap failed. reason:%s\n", strerror(errno));
            exit(4);
        }
        ftruncate(fd, 2*FILE_SIZE);
        
        
        sprintf(p, "%s", "Hello World!");
    
    } else {    // 父进程的操作
        printf("Parent:p = %s\n", p);
        
        sprintf(p, "%s", "this is message!this is message!");   // 修改内存中的值
        
        sleep (1);
        
        printf("Parent:p = %s\n", p);
        
        wait(NULL);
        
        // 同步内存中的数据到文件
        ret = msync(p, 2*FILE_SIZE, MS_SYNC);
        if (MAP_FAILED == p) {
            fprintf(stderr, "msync failed. reason:%s\n", strerror(errno));
            exit(4);
        }
        
        // 结束映射
        int ret = munmap(p, sizeof(p));
        if(ret < 0) {
            perror("munmap error. reason:");
            exit(5);
        }
    }
      
    
    return 0;
}

3). 例三,实际意义的项目用法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>


typedef struct MSG {
    char name[32];
    int age;
    char sex[32];
}Msg;

int main(int argc, char **argv) {
    
    void *mem = NULL;    
    pid_t fpid;     // 进程唯一标识符
    int ret = -1;
    int dataCount = 0;  // 记录结构体数据在文件中的个数
    
    
    if (2 > argc) {
        printf("usage: %s file.\n", argv[0]);
        exit(1);
    }
    
    // open file
    int fd = open(argv[1], O_RDWR|O_CREAT, 0664);
    if (fd < 0) {
        perror("open error. reason:");
        exit(2);
    }
    
    
    // 将文件映射到内存
    mem = mmap(NULL, sizeof(Msg), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    // 映射失败
    if (MAP_FAILED == mem) {
        fprintf(stderr, "map file failed: %s", strerror(errno));      
        exit(3);
    }

    // 修改文件的大小
    ftruncate(fd, sizeof(Msg));
    //close(fd); //关闭不用的文件描述符

  
    
    // 创建进程
    fpid = fork();
    if (fpid < 0) {
        printf("error in fork!\n");
        exit(3);
    
    } else if (0 == fpid) { // 子进程的操作
        
        printf("子进程读取共享内存中的数据打印:\n");
        
        // 循环读取内存中的所有数据打印出来
        int i = 0;
        for (; i <= 10; i++) {  // 共享内存中一个共有是一个结构体数据,依次读取出来
            Msg m;
            
            // 读取
            memcpy(&m, (Msg*)((char*)mem + (sizeof(Msg) * i)), sizeof(Msg));
            printf("name = %s\n", m.name);
            printf("age = %d\n", m.age);
            printf("sex = %s\n", m.sex);
        }  
        
    
    } else {    // 父进程的操作
            
        int i = 0;
        for (; i <= 10; i++) {
            Msg msg;
            sprintf(msg.name, "%s%d", "张某", i);
            msg.age = 20 + i;
            sprintf(msg.sex, "%s%d", "男", i);
            
            dataCount++;    // 统计个数
            
            
            // 扩大现有的内存映射
            mem = mremap(mem, sizeof(Msg) * i, sizeof(Msg) * dataCount, MREMAP_MAYMOVE);
            if (MAP_FAILED == mem) {
                fprintf(stderr, "mremap failed. reason:%s\n", strerror(errno));
                exit(4);
            }
            
            // 将数据依次拷贝到共享内存中
            memcpy((Msg*)((char*)mem + (sizeof(Msg) * i)), &msg, sizeof(Msg));
        }
        
        wait(NULL);
        printf("end---\n");
        printf("sizeof(Msg) = %ld\n", sizeof(Msg));
        
        // 修改文件的大小
        ftruncate(fd, sizeof(Msg) * dataCount);
        
        // 同步内存中的数据到文件
        ret = msync(mem, sizeof(Msg) * dataCount, MS_SYNC);
        if (MAP_FAILED == mem) {
            fprintf(stderr, "msync failed. reason:%s\n", strerror(errno));
            exit(5);
        }
        
        // 结束映射
        int ret = munmap(mem, sizeof(Msg) * dataCount);
        if(ret < 0) {
            perror("munmap error. reason:");
            exit(6);
        }
    }
      
    
    return 0;
}


三、总结

共享内存机制在项目中是常用的,且读取数据也是非常高效的,学好它也许日后对做项目会有实在用处!

淘宝分布式文件系统,用的就是mmap!

【Linux】共享内存(超详细!) 通过在物理内存申请一段共享内存空间,这个空间可以被任何进程看见。然后将内存空间映射到想要以此进行通信的进程的进程地址空间中的共享区(堆栈之间),用户就可以直接使用这个虚拟起始地址进行数据传输和接收了。 阅读详情

相关推荐

linux udp bind 返回值-1分析(端口被占用)

在linux socket通信中,我们通常用到open/bind/read/write等内部函数,那么当这些函数返回值为-1的时候,我们怎么进一步定位呢!(1)怎么打印出返回值出错的原因呢!strerror头文件。

陌上花开缓缓归以的博客 2046

mmap、msync 函数

mmap、msync

Jia ming 的日常学习笔记 4868

暴力破解pdf文档密码

相信小伙伴们都遇到这种情况;你收到了一些标题很吸引人的pdf文档 或者是重要数据泄露 学习资料总结,再或者是某机构的押题密卷,打开pdf却提示你要输入密码 因为不知道密码 只能弃罢。我就遇到过这种情况 网友发来了标题为某天一的押题密卷 ,我也想看看这个卷子的题怎么样,因为被设置了密码 我尝试用6到10位的纯数字组合来暴力破解这个pdf,结果三 四个小时都没找到正确的密码。有些网友为破解这个密码 请某宝某多来破解 也都没有破解成功。

shelter1234567的博客 1万+

Linux系统(进程间通信)mmap-msync函数

msync函数的作用:实现磁盘中的内容和共享的内容一致。头文件:#includemsync函数:int msync( void * addr,size_t len ,int flags);addr:文件映射到进程空间的地址位置;len:空间的大小;flags:参数设置,可以为MS_ASYNC(异步)/ MS_SYNC(同步)异步,将会立刻返回,而同步时候,调用将等更新完成后返回成功return 0,失败return -1;

博客 1029

发那科FOCAS 1/2文档——核心库文件

在开始开发前,明确目标 CNC 的具体型号、连接方式(HSSB/TCP/IP)、软件版本和配置(如轴数、程序号位数),并据此正确配置开发环境和编译选项。开发时需要包含相应的头文件。这是开发跨平台应用时需要特别注意的地方。这是开发应用时必须了解的核心文件。

snailiot的博客 1019

Unity透明视频播放全攻略:AVPro Video插件实战与避坑指南

在游戏开发与交互式内容创作中,视频播放是常见的多媒体需求。其核心原理在于通过解码器将压缩的视频数据流转换为连续的图像帧,再经由图形API渲染至屏幕。这项技术的价值在于能够高效地呈现动态视觉内容,广泛应用于游戏过场动画、UI特效、AR/VR虚拟展示等场景。当需求进阶到需要背景透明的视频时,技术实现复杂度显著增加,这涉及到对视频Alpha通道的编码、解码与渲染管线的特殊处理。本文聚焦于Unity引擎中实现高质量透明视频播放的工程实践,深入解析了AVPro Video这一专业插件如何解决原生VideoPlayer

weixin_30368981的博客 376

Linux进程间通讯(IPC机制)——共享内存与消息队列

多个进程在物理内存上有内存空间是共享的,多个进程在各自的逻辑地址空间写入数据,或者获取数据,使用同一个物理空间,不需要数据的拷贝。

Orange_42的博客 211

VC Spyglass CDC实战:从TCL脚本到SDC约束的完整避坑指南

本文是一份针对数字IC验证工程师的VC Spyglass CDC实战指南。文章详细阐述了从TCL脚本环境搭建、SDC约束编写到典型CDC违规(如SETUP_PORT_UNCONSTRAINED)诊断与修复的完整流程,旨在帮助读者高效避坑,深入理解工具逻辑,从而完成可靠的时钟域交叉验证。

go5gopher的博客 382

Linux进程间通讯概述

Linux进程间通讯概述

安迪西_嵌入式攻城狮 480

【WRF实操】四层嵌套网格设计-以粤港澳大湾区GBA为例

四层嵌套网格设计-以粤港澳大湾区GBA为例

WW、forever的博客 819

GEE聚类分析:基于sentinel-2 影像进行 K-Means 聚类分析

这篇博客介绍了如何使用 Google Earth Engine (GEE) 进行 K-Means 聚类分析,以识别感兴趣区域(ROI)内的不同地表覆盖类型。文章详细说明了从定义用户参数、准备参考区域、加载和预处理 Sentinel-2 图像,到训练 K-Means 聚类器并对图像进行聚类的完整流程。代码示例展示了如何设置参数、采样像素数据、训练聚类器,并将结果可视化。最后,还提供了将聚类结果导出到 Google Drive 的选项。通过该教程,用户可以掌握在 GEE 平台上进行地理数据聚类分析的基本方法。

此星光明博客 262

进程之间的同步方式

<br />windows下的进程同步和通讯主要靠各种内核对象(互斥,信号,事件,文件影射),比较有特色的是窗口消息,dde/ole/com也可以使用rpc机制进行进程间通讯。同步和通讯的目的是一样的,实现进程间数据共享,同步只是为了做到处理协同。共享内存在Win9X平台上是有的,在NT内核以后就没有这一说了,因为进程地址空间不再有共用部分<br />   <br />   <br /> Linux  :通信就是说进程之间传递数据。常见的方法有   pipe(管道),FIFO(命名管道),socket(套接

xiaofei0859的专栏 5019

万字长文,YOLOv8 yaml 文件解析 | 一文搞定 YOLOv8 分类任务,检测任务,分割任务,关键点任务

YOLOv8 yaml 文件解析 | 一文搞定 YOLOv8 分类任务,检测任务,分割任务,关键点任务

Coding... 1万+

探索Linux共享内存与信号通讯:开启高效进程协作之门

例如,当一个进程向共享内存中写入数据后,可以发送一个信号通知其他进程数据已更新,其他进程接收到信号后再读取共享内存中的数据。创建共享内存对象:使用 shmget 函数创建一个新的共享内存段,或者获取一个已存在的共享内存段。- 自定义处理函数:使用 signal 函数或 sigaction 函数注册自定义的信号处理函数,当进程接收到信号时,会调用相应的处理函数。映射共享内存到进程地址空间:使用 shmat 函数将共享内存段映射到调用进程的地址空间,这样进程就可以像访问普通内存一样访问共享内存。

2401_87067267的博客 1461

CloudCompare源码分析之CCCoreLib模块:ICP算法(阅读经典)

CloudCompare源码分析之CCCoreLib模块:ICP算法(阅读经典)

dayuhaitang1的博客 2549

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Linux学习------进程间通讯

测试可以多次p 和 v操作,多次v操作锁箱里的钥匙会增加,但是无论如何如果没有钥匙的时候p操作会阻塞。本质是一间房子被锁住,从钥匙箱子拿钥匙去开门,一把锁可以有很多钥匙。

dmq9921的博客 1659

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Linux进程间通讯----管道,消息队列,信号量,共享内存

Linux进程间通讯手段主要有:管道,消息队列,信号量,共享内存,不同网络之间之间通讯会使用socket。 管道: 管道主要通过文件系统的辅助来实现通讯,有两种方式:有名管道与无名管道。 有名管道在磁盘上有一个管道文件标识,但是其进行数据交互时,并不占用磁盘空间,仅在磁盘上存在一个文件i-node节点信息。 有名管道的创建: mafifo filename或 int mkfifo(char* f...

weixin_43611545的博客 475

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

linux进程间的那些事儿

写这篇文章之前,我对linux的进程间通讯还是有些畏惧的,不过看了一些其它文章之后,觉得linux进程间远比我学到的要难得多,首先来说,linux下线程的概念被淡化了,线程又名轻量级进程。线程机制是现代编程技术中常用的一种抽象,提供了在同一程序中共享内存地址控件的一组线程。这些线程可以共享打开的文件和其它资源。线程机制支持并发程序涉及技术,可以保证真正并行处理。linux实现线程的机制非常独特,从...

logens的专栏 309

Linux 进程间通讯方式(6种)

Linux进程间通讯方式:6种 1.传数据:半双工管道(单向传输) FIFO管道文件(又称半双工管道) 消息队列 共享内存 套接字 2.不能传数据:信号量

weixin_45732201的博客 223

进程间的通讯(IPC)方式

为什么要进行进程间的通讯(IPC (Inter-process communication)) 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。 资源共享:多个进程...

bbbeoy的专栏 229

【Linux】共享内存

共享内存本质上就是内存中的一块区域,用于进程间通信使用。该内存空间由操作系统分配与管理。与文件系统类似的是,操作系统在管理共享内存时,不仅仅有内存数据块,同时还会创建相应结构体来记录该共享内存属性,以便于管理。因此,共享内存不只有一份,可以根据需求申请多个。进程之间进行通信的时候,会获取到共享内存的地址,写端进程写入数据,读端进程通过直接访问内存完成数据读取。

m0_73911405的博客 1240

linux bible 第五章 进程间通讯机制

 第五章 进程间通讯机制 进程在核心的协调下进行相互间的通讯。Linux支持大量进程间通讯(IPC)机制。 除了信号和管道外,Linux还支持Unix系统V中的IPC机制。 5.1 信号 信号是Unix系统中的最古老的进程间通讯方式。它们用来向一个或多个进程发 送异步事件信号。信号可以从键盘中断中产生,另外进程对虚拟内存的非法存 取等系统错误环境下也会有信号产生。信号还被shell程序用来向其

499

进程间通讯(IPC)

进程间通讯是指两个或多个进程之间交换信息的机制。每个进程在系统中都是独立的资源管理单元,它们之间的资源是相互隔离的。尽管如此,进程之间仍然需要进行数据传输、同步或其他形式的消息交换,因此进程间通讯机制应运而生。在Linux中,进程间通讯是一个至关重要的功能,它使得多个进程能够协同工作,共享数据和资源。不同的IPC方式各有优缺点,开发者可以根据实际需求选择合适的通讯机制。无论是简单的管道,还是更复杂的共享内存和消息队列,IPC都为Linux系统提供了强大的进程间协作能力。

正经话的博客 1243

Linux进程间通讯

文章目录管道信号量基本概念 管道 信号量 基本概念 临界资源 任意时刻只能被一个进程(线程)访问的资源称为临界资源 临界区 当多个进程同时访问系统上的某个资源时,比如同时修改某个文件,就要考虑进程的同步问题,以确保任一时刻只有一个进程拥有对此资源的操作权限,对临界资源进行访问的这段代码就称之为临界区。 原子操作 指的是该操作不会在执行完成之前被其它操作或者任务所打断,也就是说不会执行的最小单...

weixin_43825537的博客 647

【IPC通信--共享内存mmap】

mmap共享内存机制是一种高效的进程间通信方式,可以实现多个进程之间的数据共享。它利用虚拟内存的特性,将文件或者设备映射到内存中,实现了数据的实时共享。通过使用mmap,可以提高程序的效率和响应速度,适用于需要大量数据交换的场景。共享内存映射之mmap()函数详解_51CTO博客_内存映射mmap深入理解内存映射:mmap映射的背后原理以及和共享内存的差异 - 知乎 (zhihu.com)

j8267643的博客 1976

Linux进程间通讯 -- 管道

在 Linux 中每个进程都具备独立的进程地址空间,对每个进程的独立地址空间进行划分,在0G - 3G部分被划分为用户空间,而3G - 4G部分被划分为内核地址空间。在进行地址映射的时候,每个进程的用户空间在实际物理空间上将被映射到多个地址空间,而多个进程的内核空间将会被被映射到同一块区域,因此多个进程之间具备相同的内核地址空间,通过此共同的内核地址空间实现线程间数据交互即为进程间通讯,也即IPC。当管道中数据被写满时,再次往管道内写入数据会进入阻塞等待状态,直至有数据从管道的读端被读走。

爱出名的狗腿子 1414
上一篇: C/C++ Linux进程操作
下一篇: Linux进程间通信 - 信号(signal) 与 管道(pipe) 与 消息队列
cpp_learners
博客等级 码龄7年 1526粉丝 274原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值