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

news2024/9/21 8:04:24

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

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

两种常用共享内存方式

  • 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!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/459376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb02(servlet)

目录 一.servlet 1.1 什么是servlet? 1.2 实现接口,初始代码 1.3 学会配置和映射 1.4 掌握servlet的生命周期 生命周期的各个阶段 1.5 获取servlet初始化参数和上下文参数 1.5.1 初始代码 推荐使用 1.5.2 初始化参数 1.5.3 上下文参数 1.6 servlet应用:处理用户登…

融云 CTO 岑裕:出海技术前沿探索和排「坑」实践

在本文中&#xff0c;你将看到以下内容&#xff1a; 全球通信网络在接入点、链路加速、服务商、协议等层面的动态演进&#xff1b; 进入到具体市场&#xff0c;禁运国、跨国拦截、区域一致性差等细节“坑点”如何应对&#xff1b; 融云如何从技术侧帮助开发者应对本地化用户体…

首发支持NOA的单征程3行泊一体域控,这家Tier1开“卷”

NOA正成为智能驾驶下半场的竞争焦点之一。 显然&#xff0c;NOA所处的L2/L2区间&#xff0c;在技术上仍然属于驾驶辅助领域&#xff0c;但在传统L2级ADAS功能的基础上增强了部分场景的功能ODD。在部分政策允许的国家和地区&#xff0c;可以实现有条件的「解放双手」。 高工智…

centos搭建vue3运行环境

一、安装 nodejs 1.下载&解压 Node.js官网&#xff1a;Download | Node.js cd /usr/local/src/wget -c https://nodejs.org/dist/v16.18.1/node-v16.18.1-linux-x64.tar.xz xz -d node-v16.18.1-linux-x64.tar.xz tar -xf node-v16.18.1-linux-x64.tarmv node-v…

threejs学习随笔(入门篇)

前言&#xff1a;three.js和webgl Three.js经常会和WebGL混淆&#xff0c; 但也并不总是&#xff0c;three.js其实是使用WebGL来绘制三维效果的。 WebGL是一个只能画点、线和三角形的非常底层的系统. 想要用WebGL来做一些实用的东西通常需要大量的代码&#xff0c; 这就是Thre…

C语言函数大全-- p 开头的函数

C语言函数大全 本篇介绍C语言函数大全-- p 开头的函数 1. perror 1.1 函数说明 函数声明函数功能void perror(const char *s);用于将当前错误码对应的错误信息打印到标准输出设备&#xff08;通常是终端&#xff09;。 参数&#xff1a; s &#xff1a; 用于描述错误类型或…

班级页面设计——【3-相关活动页面】内容使用HTML以及css和Javascripts技术

系列文章目录 班级页面设计——【1-登陆注册页面】_网页制作实现登录注册 班级页面设计——【2-主界面部分】_班级首页展示 文章目录 系列文章目录 前言 一、页面效果介绍 1.1、页面展示 1.2、简单介绍 二、代码展示部分 2.1、html代码部分 2.2、css代码部分 前言 …

C++(继承中)

目录&#xff1a; 1.基类和派生类对象赋值转换 2.派生类当中的6个默认成员函数 --------------------------------------------------------------------------------------------------------------------------- 派生类对象可以赋值给 基类的对象/基类的指针/基类的引用&am…

“量子计算+个性化医疗”!富士通和BSC利用张量网络推进新研究

​ &#xff08;图片来源&#xff1a;网络&#xff09; 富士通和巴塞罗那超级计算中心(BSC)正在签署一项合作协议&#xff0c;通过利用临床数据促进个性化医疗&#xff0c;并使用张量网络推进量子模拟技术。 双方将于2023年5月开始联合研究&#xff0c;第一个合作项目旨在利用不…

CSGO搬砖,每天1-2小时,23年最强副业非它莫属(内附操作流程)

自从我学会了CSGO搬运&#xff0c;我发现生活也有了不小的改变&#xff0c;多了一份收入&#xff0c;生活质量也就提高了一份。 其实刚接触CSGO&#xff0c;我压根就不相信这么能挣钱&#xff0c;因为在印象中&#xff0c;游戏供玩家娱乐竞技的&#xff0c;作为我这种技术渣渣…

VUE3如何定义less全局变量

默认已经安装好了less&#xff0c;这里不过多讲。 &#xff08;1&#xff09;首先我们需要下载一个插件依赖&#xff1a; npm i style-resources-loader --save-dev &#xff08;2&#xff09;VUE3里配置vue.config.js文件内容 代码&#xff1a; const path require("p…

U盘安装Windows11和ubuntu20.04双系统

准备&#xff1a; 一台PC电脑&#xff08;我的是两个固态硬盘&#xff09; 一个U盘&#xff08;最好不小于32G&#xff09; 下载安装工具&#xff1a; 老白菜u盘启动盘制作工具_u盘启动_u盘装系统下载尽在老白菜官网 最新UltraISO官方免费下载 - UltraISO软碟通中文官方网…

数据结构与算法(九) 树

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲树 树是一种常见的数据结构&#xff0c;其定义为&#xff1a;由有限个节点组成的具有层次关系的集合 解决树问题的关键是递归&#xff0c;递归的关键是分解子问题 对于树来说&#xff0c;递归函数只要考虑对单个节点如何处…

波奇学Linux:Linux的认识和云服务器使用

在讲Linux前&#xff0c;我们先来理解计算机&#xff1a; 计算机&#xff1a;输入->算法->输出 举个栗子&#xff1a; pritnf :输出到屏幕&#xff08;硬件&#xff09;上 我们在计算机所有的行为都会转为硬件行为。 再进一步理解,我们打开visual studio后&#xff0c;敲…

BPF技术学习与整理

目录 eBPF是什么&#xff1f; eBPF是做什么的&#xff1f;可以解决什么问题&#xff1f; eBPF可以带来的解决方案是什么&#xff1f; eBPF的技术点 eBPF hookeBPF MapeBPF Helper FunctioneBPF有什么限制吗&#xff1f; 前言 21年因为项目需求而要开发一个工具&#xff0c;可以…

如何通过docker启动一个本地springboot的jar包

一、构建本地jar包 进入到项目目录下执行如下命令 mvn -e clean package -Dmaven.test.skiptrue或者直接在idea中打包 得到target文件夹 进入到target文件夹得到jar包 二、创建Dockerfile文件 新建Dockerfile文件&#xff0c;内容如下 FROM openjdk:8-jre MAINTAINER ja…

硬盘坏掉之后

文章目录 背景解决方案数据丢失软件安装 总结 背景 前一段时间&#xff0c;我的电脑突然就开不了机了&#xff0c;进入安全模式之后&#xff0c;发现硬盘无法读取&#xff0c;大概率是硬盘坏掉了。电脑是 MacBook&#xff0c;自己不太好换。于是跑到华强北&#xff0c;找了一家…

电脑无论是连接热点,还是公共网络,qq、微信都能用,就是不能上网,现分享解决办法如下。

这里写自定义目录标题 问题引入&#xff1a;解决办法1、打开电脑的控制面板2、点击 “网络和internet”3、点击 “internet 选项”4、点击 “连接”5、点击 “局域网设置”6、按照下面操作 问题引入&#xff1a; 在魔法使用网站之后&#xff0c;忘记关闭 clash 按钮就关闭电脑&…

Spring-学习修改尚硅谷最新教程笔记

二、Spring 1、Spring简介 1.1、Spring概述 官网地址&#xff1a;https://spring.io/ Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Jav…

数据湖Iceberg-SparkSQL集成(4)

文章目录 数据湖Iceberg-SparkSQL集成一、环境准备安装Spark 二、Spark配置Catalog2.1在配置文件中添加HiveCatalog与HadoopCatalog配置&#xff08;一劳永逸&#xff09;2.2使用spark-sql连接Hive Catalog2.3使用spark-sql连接Hadoop Catalog 三、SQL操作3.1 创建表创建分区表…