Linux下的系统编程——共享存储映射(十)

news2024/9/21 23:34:06

前言:

mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。

目录

 一.文件间进程通信:

 二、存储映射I/O:

三、mmap函数: 

1.创建共享内存映射:

2.释放映射区:

*3.使用注意事项:

*4.mmap函数的保险调用方式:

5.mmap父子进程通信:

6.无血缘关系进程间 mmap 通信:                  


 一.文件间进程通信:

打开的文件是内核中的一块缓冲区。多个无血缘关系的进程,可以同时访问该文件。

两个完全独立没有血缘关系的进程文件之间也可以完成进程间的通信

test1.c 先执行,将数据写入文件test.txt 

 

/*
 * 先执行,将数据写入文件test.txt
 */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#define N 5

int main(void)
{
    char buf[1024];
    char *str = "--------------secesuss-------------\n";
    int ret;

    int fd = open("test.txt", O_RDWR|O_TRUNC|O_CREAT, 0664);

    //直接打开文件写入数据
    write(fd, str, strlen(str));
    printf("test1 write into test.txt finish\n");

    sleep(N);

    lseek(fd, 0, SEEK_SET);
    ret = read(fd, buf, sizeof(buf));
    ret = write(STDOUT_FILENO, buf, ret);

    if (ret == -1) {
        perror("write second error");
        exit(1);
    }

    close(fd);

    return 0;
}

test2.c   后执行,尝试读取另外一个进程写入文件的内容

/*
 * 后执行,尝试读取另外一个进程写入文件的内容
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

int main(void)
{
    char buf[1024];
    char *str = "----------test2 write secesuss--------\n";
    int ret;

    sleep(2);   //睡眠2秒,保证test1将数据写入test.txt文件

    int fd = open("test.txt", O_RDWR);

    //尝试读取test.txt文件中test1写入的数据
    ret = read(fd, buf, sizeof(buf));   

    //将读到的数据打印至屏幕
    write(STDOUT_FILENO, buf, ret);

    //写入数据到文件test.txt中, 未修改读写位置
    write(fd, str, strlen(str));

    printf("test2 read/write finish\n");

    close(fd);

    return 0;
}

 

 二、存储映射I/O:

        存储映射I/o(Memory-mapped l/o)使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址(指针)完成l/o操作。
        

        使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过 mmap函数来实现。

三、mmap函数: 

1.创建共享内存映射:

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

参数:

        addr     指定映射区的首地址。通常传NULL,表示让系统自动分配

        length:  共享内存映射区的大小。(<= 文件的实际大小)

        prot:     共享内存映射区的读写属性

        PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE

        flags:   标注共享内存的共享属性

        MAP_SHARED、MAP_PRIVATE

        fd:          用于创建共享内存映射区的那个文件的文件描述符。

        offset: 默认0,表示映射文件全部。偏移位置。需是 4k 的整数倍

返回值:

        成功:映射区的首地址。

        失败:MAP_FAILED (void*(-1)), errno

2.释放映射区:

int munmap(void *addr, size_t length);        

    addr:mmap 的返回值

    length:大小

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc,char *argv[])
{

	char *p = NULL;
	int fd;

    //打开或者创建tetsmap文件
	fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);
	if(fd == -1){
		sys_err("open error");
	}

	/*两个函数等价于ftruncate()函数
	 	lseek(fd,10,SEEK_END);
		write(fd,"\0",1);
	*/

	ftruncate(fd,20);
	int len = lseek(fd,0,SEEK_END);

	p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if(p == MAP_FAILED){
		sys_err("mmap error");
	}

    //使用p对文件进行读写操作

	strcpy(p,"hello mmap\n");

	printf("-------%s\n",p);//写操作

	int ret = munmap(p,len);
	if(ret == -1){
		sys_err("munmap error");
	}
	return 0;
}

*3.使用注意事项:

语法
open()方法语法格式如下:

os.open(file, flags[, mode]);

参数
file – 要打开的文件

flags – 该参数可以是以下选项,多个使用 “|” 隔开:

O_RDONLY: 以只读的方式打开
O_WRONLY: 以只写的方式打开
O_RDWR : 以读写的方式打开
O_NONBLOCK: 打开时不阻塞
O_APPEND: 以追加的方式打开
O_CREAT: 创建并打开一个新文件
O_TRUNC: 打开一个文件并截断它的长度为零(必须有写权限)
O_EXCL: 如果指定的文件存在,返回错误
O_SHLOCK: 自动获取共享锁
O_EXLOCK: 自动获取独立锁
O_DIRECT: 消除或减少缓存效果
O_FSYNC : 同步写入
O_NOFOLLOW: 不追踪软链接



1)PROT_READ:表示内存段内的内容可写;

2)PROT_WRITE:表示内存段内的内容可读;

3)PROT_EXEC:表示内存段中的内容可执行;

4)PROT_NONE:表示内存段中的内容根本没法访问。

  (1). 用于创建映射区的文件大小为 0,实际指定非0大小创建映射区,出 “总线错误”

	fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);  //O_RDWR : 以读写的方式打开
	if(fd == -1){
		sys_err("open error");
	}

    //ftruncate(fd,20);
	//int len = lseek(fd,0,SEEK_END);

	int len = 20;

    //PROT_READ:表示内存段内的内容可写;   PROT_WRITE:表示内存段内的内容可读

    p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      //可读可写

 

    (2) 用于创建映射区的文件大小为 0,实际制定0大小创建映射区, 出 “无效参数”

	fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);    //O_RDWR : 以读写的方式打开
	if(fd == -1){
		sys_err("open error");
	}
    
    //ftruncate(fd,20);
	//int len = lseek(fd,0,SEEK_END);

	int len = 0;

    //PROT_READ:表示内存段内的内容可写;   PROT_WRITE:表示内存段内的内容可读

    p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      //可读可写

   

    (3). 用于创建映射区的文件读写属性为,只读。映射区属性为 读、写。 出 “无效参数”。

	fd = open("testmap",O_RDONLY|O_CREAT|O_TRUNC,0644);    //O_RDONLY: 以只读的方式打开
	if(fd == -1){
		sys_err("open error");
	}

	ftruncate(fd,20);
	int len = lseek(fd,0,SEEK_END);

	//int len = 0;

    //PROT_READ:表示内存段内的内容可写;   PROT_WRITE:表示内存段内的内容可读

    p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      //可读可写

    (4). 创建映射区,需要read权限。当访问权限指定为 “共享”MAP_SHARED是, mmap的读写权限,应该 <=文件的open权限。    只写不行。

	//fd = open("testmap",O_RDONLY|O_CREAT|O_TRUNC,0644);
	fd = open("testmap",O_WRONLY);    //文件的只写
	if(fd == -1){
		sys_err("open error");
	}
	ftruncate(fd,20);
	int len = lseek(fd,0,SEEK_END);

	//int len = 0;

	p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);  //文件只写

 

	fd = open("testmap",O_RDWR);    //文件的读写

 

注意:当文件是读写的时候可以写,当文件是只读的时候可以是只读。当文件是只写的时候不能写 

    (5). 文件描述符fd,在mmap创建映射区完成即可关闭。后续访问文件,用地址访问。

	//fd = open("testmap",O_RDONLY|O_CREAT|O_TRUNC,0644);
	fd = open("testmap",O_RDWR);
	if(fd == -1){
		sys_err("open error");
	}
	ftruncate(fd,20);
	int len = lseek(fd,0,SEEK_END);

	//int len = 0;

	p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);//文件只写
	if(p == MAP_FAILED){
		sys_err("mmap error");
	}
	
	close(fd);    //创建映射区完成即可关闭

  (6). offset 必须是 4096的整数倍。(MMU 映射的最小单位 4k

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,1000);

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,4096);

 

    (7). 对申请的映射区内存,不能越界访问。 

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);

               .......

strcpy(p+len+4096*4,"hello mmap\n");

  

    (8). munmap用于释放的地址,必须是mmap申请返回的地址。

p = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);

               .......

strcpy(p++,"hello mmap\n");

  

 解决方法:

 char *temp = p;
 strcpy(temp++,"hello mmap\n");

  

    (9). 映射区访问权限为 “私有”MAP_PRIVATE, 对内存所做的所有修改,只在内存有效,不会反应到物理磁盘上。

	int fdfd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);
	//fd = open("testmap",O_RDWR);
	
	if(fd == -1){
		sys_err("open error");
	}
	ftruncate(fd,20);
	int len = lseek(fd,0,SEEK_END);
	

	//int len = 0;
	p = mmap(NULL,len,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0);//可读可写

  

    (10).映射区访问权限为 “私有”MAP_PRIVATE, 需要open文件时,有读权限用于创建映射区即可。

    int fd;
	//int fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);
	fd = open("testmap",O_RDONLY);    //O_RDONLY: 以只读的方式打开
	
	if(fd == -1){
		sys_err("open error");
	}
	//ftruncate(fd,20);
	int len = lseek(fd,0,SEEK_END);
	

	//int len = 0;
	p = mmap(NULL,len,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0);//可读可写

	if(p == MAP_FAILED){
		sys_err("mmap error");
	}

*4.mmap函数的保险调用方式:

    1. fd = open("文件名", O_RDWR);

    2. mmap(NULL, 有效文件大小PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

5.mmap父子进程通信:

        父子等有血缘关系的进程之间也可以通过 mmap建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数flags:

        MAP_PRIVATE:(私有映射)父子进程各自独占映射区;

        MAP_SHARED:(共享映射)父子进程共享映射区;
 

父进程创建映射区,然后fork子进程,子进程修改映射区内容,而后,父进程读取映射区内容,查验是否共享。

思路:

父进程先创建映射区。 open( O_RDWR) mmap( MAP_SHARED );

 指定 MAP_SHARED 权限

 fork() 创建子进程。

 一个进程读, 另外一个进程写

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

//全局变量
int var = 100;

int main(void)
{
	int *p;
	pid_t pid;

	int fd;
	fd = open("temp",O_RDWR|O_CREAT|O_TRUNC,0644);
	if(fd < 0){
		perror("open error");
		exit(1);
	}
	unlink("temp");
	ftruncate(fd,4);

    p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);    //MAP_SHARED共享映射
  //p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);   //MAP_PRIVATE私有映射

	if(p == MAP_FAILED){        //注意:不是p == NULL
		perror("mapp error");
		exit(1);
	}

	close(fd);           //映射区建立完毕,即可关闭文件

	pid = fork();        //创建子进程

	if(pid == 0){        //子进程
		*p = 2000;       //写共享内存
		var = 1000;
		printf("child *p = %d,var = %d\n",*p,var);
	}else if(pid > 0){    //父进程
		sleep(1);
		printf("parent *p = %d,var = %d\n",*p,var);    //读共享内存
		wait(NULL);

		int ret = munmap(p,4);    //释放映射区
		if(ret == -1){
			perror("munmap error");
			exit(1);
		}
	}

	return 0;
}

//p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);    //MAP_SHARED共享映射
  p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);   //MAP_PRIVATE私有映射

 

6.无血缘关系进程间 mmap 通信:                  

    两个进程 打开同一个文件,创建映射区。

    指定flags 为 MAP_SHARED。

    一个进程写入,另外一个进程读出。

   【注意】:无血缘关系进程间通信。

                                mmap:数据可以重复读取。

                                fifo:数据只能一次读取。

匿名映射:只能用于血缘关系进程间通信

p = (int *)mmap(NULL, 40,PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

写端:

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

struct student {
    int id;
    char name[256];
    int age;
};

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc, char *argv[])
{
    struct student stu = {1, "xiaoming", 18};
    struct student *p;
    int fd; 

    fd = open("test_map", O_RDWR|O_CREAT|O_TRUNC, 0664);
//    fd = open("test_map", O_RDWR);
    if (fd == -1)
        sys_err("open error");

    ftruncate(fd, sizeof(stu));

    p = mmap(NULL, sizeof(stu), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
        sys_err("mmap error");

    close(fd);

    while (1) {
        memcpy(p, &stu, sizeof(stu));
        stu.id++;
        sleep(2);
    }
    
    munmap(p, sizeof(stu));

	return 0;
}

读端:

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

struct student {
    int id;
    char name[256];
    int age;
};

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc, char *argv[])
{
    struct student stu;
    struct student *p;
    int fd; 

    fd = open("test_map", O_RDONLY);
    if (fd == -1)
        sys_err("open error");

    p = mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
        sys_err("mmap error");

    close(fd);

    while (1) {
        printf("id= %d, name=%s, age=%d\n", p->id, p->name, p->age);
        sleep(1);
    }
    
    munmap(p, sizeof(stu));

	return 0;
}

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

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

相关文章

数据结构与算法:概述

目录 算法 评价标准 时间的复杂度 概念 推导原则 举例 空间的复杂度 定义 情形 运用场景 数据结构 组成方式 算法 在数学领域&#xff0c;算法是解决某一类问题的公式和思想&#xff1b; 计算机科学领域&#xff0c;是指一系列程序指令&#xff0c;用于解决特定的…

30 秒使用 Sealos 搭建个人密码管理器 Vaultwarden

我与 LastPass 的曲折恋情 超过 8 年网龄的我&#xff0c;注册过很多网站帐号&#xff0c;每个网站的密码我都用不同的复杂密码。一开始我全靠脑力记忆这些密码&#xff0c;后来渐渐觉得记起来很困难&#xff0c;就记录在笔记本上。但是随着时间推移&#xff0c;我发现这种方法…

什么样的蓝牙耳机戴着舒服,佩戴舒适的蓝牙耳机推荐

什么样的蓝牙耳机戴着舒服&#xff1f;相信大家肯定有这么一个困扰&#xff0c;就是在入耳式耳机佩戴时间久了&#xff0c;总感觉耳道内部不舒服&#xff0c;那么今天我要向大家介绍一种备受骨传导爱好者推崇的神奇装置——骨传导耳机。首先&#xff0c;让我们来了解一下骨传导…

亚信安慧荣膺“信创工委会技术活动单位”

近日&#xff0c;以亚信科技AntDB数据库团队为基础组建而成的湖南亚信安慧科技有限公司&#xff08;简称&#xff1a;亚信安慧&#xff09;被中国电子工业标准化技术协会、信息技术应用创新工作委员会(简称&#xff1a;信创工委会)授予“信息技术应用创新工作委员会技术活动单位…

小程序分销机制介绍,小程序二级分销功能有哪些?

为什么有越来越多的用户选择使用小程序&#xff1f;跟“高大上”的APP相比&#xff0c;小程序不仅可以减少下载安装的复杂流程&#xff0c;还具备操作便捷、沉淀私域数据的优势。蚓链分销小程序具备裂变二维码、实时分佣、分销身份升级、层级分佣、商品个性化佣金设定等功能&am…

geopandas 笔记: datasets 数据集

geopandas 自带的几个数据集 1 世界各个国家 import geopandas as gpd import pandas as pdpd.set_option(display.max_rows,None) gpd.read_file(gpd.datasets.get_path(naturalearth_lowres)) pop_est人口数量continent国家所在的大陆name国家的名称iso_a3国家的三个字母的…

谷歌霸屏推广怎么做?

答案是&#xff1a;可以使用GLB外推技术实现谷歌霸屏推广。 谷歌霸屏推广是一种Google SEO策略&#xff0c;旨在确保品牌或关键词在Google搜索结果的第一页上占据多个位置。 正确地执行此策略可以大大提高品牌的在线曝光度&#xff0c;从而增加流量和潜在客户。 那么&#x…

运动耳机哪种好、运动戴的蓝牙耳机推荐

作为一名运动爱好者&#xff0c;自然要有一款专业的运动耳机&#xff0c;运动耳机的重要作用就是它能帮我们缓解枯燥运动时的乏味&#xff0c;还能提高运动锻炼的效果。热爱运动的我&#xff0c;最喜欢就是运动音乐随行了&#xff0c;在用过众多蓝牙耳机之后&#xff0c;才明白…

大数据Flink(七十五):SQL的Session 窗口(SESSION)

文章目录 SQL的Session 窗口(SESSION) 一、Session 窗口定义 二、实际案例 三、注意事项 SQL的Session 窗口(SESSI

2023年7月京东白酒行业品牌销售排行榜(京东数据平台)

鲸参谋监测的京东平台7月份白酒市场销售数据已出炉&#xff01; 鲸参谋数据显示&#xff0c;今年7月份&#xff0c;京东平台白酒的销量为210万&#xff0c;同比增长21%&#xff1b;销售额将近19亿&#xff0c;同比增长超过85%。可以看到&#xff0c;同比去年同期&#xff0c;今…

从零开始探索C语言(七)----enum枚举

枚举是 C 语言中的一种基本数据类型&#xff0c;用于定义一组具有离散值的常量&#xff0c;它可以让数据更简洁&#xff0c;更易读。 枚举类型通常用于为程序中的一组相关的常量取名字&#xff0c;以便于程序的可读性和维护性。 定义一个枚举类型&#xff0c;需要使用 enum 关…

OPPO/真我手机ColorOS13系统解账户锁-移除手机密码图案锁方法

在搞机之前&#xff0c;请确定自己的手机不是非法获取&#xff0c;本文只讲叙ColorOS13系统解锁方法&#xff0c;仅为个人测试研究出来的经验&#xff0c;未对官方系统进行任何修改。只推荐专业维修师傅从维修的角度进行解锁&#xff0c;不推荐个人用户对非自己的手机进行非法破…

索尼 toio™应用创意开发征文|惊喜魔方

前言&#xff1a; 医院中每一次新的婴儿啼哭&#xff0c;代表着一个新的生命诞生。这时候大家围在新生命的旁边&#xff0c;有爸爸、妈妈、爷爷、奶奶、外公、外婆、七大姑八姨等等。我们总是对新的生命充满期待&#xff0c;期待他长大&#xff0c;长成我们羡慕的样子&#xff…

李跳跳使用、逆向、脱壳和原理介绍

加我拉你入群黑糖安全公众号 前言 你可以独善其身 但你不能兼济天下 简介 其实这部分是使用教程&#xff0c;github上面有备份的下载链接&#xff0c;只是可能不更新了&#xff0c;V2.2安装之后一把快刀&#xff0c;很简洁的界面点击界面里面的开启按钮即可&#xff0c;我这里…

YOLOV7改进-对小目标有提点的Omni-Dimensional Dynamic Convolution

ODConv 比普通卷积时间长&#xff0c;对小目标也有作用 1、models下建立文件&#xff0c;复制进来 2、yolo.py文件下导入模块&#xff0c;下面添加解析函数 3、改下面的1->2&#xff0c;不改会报错 4、修改配置文件&#xff0c;改网络模型&#xff0c;conv->ODConv2d 5…

​怎么安全无损地将操作系统转移到固态硬盘?

为什么需要转移系统到固态硬盘&#xff1f; 现如今&#xff0c;许多用户想要将自己的操作系统转移到固态硬盘&#xff0c;这是为什么呢&#xff1f;我们在下面向大家简单地介绍了一下迁移系统的优势&#xff1a; ​提升计算机性能&#xff1a;硬盘&#xff08;HDD…

VM安装RedHat7虚机ens33网络不显示IP问题解决

1、今天在VMware中安装RedHat7.4虚拟机&#xff0c;网络连接使用的是 NAT 连接方式&#xff0c;刚开始安装成功之后输入ifconfig 还能看到ens33自动分配的IP地址&#xff0c;但是当虚机关机重启后&#xff0c;再查看IP发现原来的ens33网络已经没有了&#xff0c;只变成了这两个…

Excel周报制作

Excel周报制作 文章目录 Excel周报制作一、理解数据二、数据透视表三、常用函数1.sum-求和2.sumif-单条件求和3.sumifs-多条件求和4.sum和subtotal的区别5.if函数6.if嵌套7.vlookup函数和数据透视表聚合8.index和match函数 四、周报开发五、报表总览 一、理解数据 这是一个线上…

内存泄露排查思路

1、泄露情况 启动闪退运行一段时间宕机 2、排查步骤 获取堆内存快照dump使用VisualVM分析dump文件通过查看堆信息的情况&#xff0c;定位内存溢出问题 jmap -dump:formatb,fileheap.hprof pid -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath输出路径 3、在VisualVM中分…

公网访问群辉相册Synology Photos ,快速搭建群辉相册同时远程访问【无公网IP内网穿透】

文章目录 前言本教程解决的问题是&#xff1a;按照本教程操作完成能够达到的效果是&#xff1a;1.在群辉中下载并安装Synology Photos套件2.设置共享文件夹3.添加您想共享的照片4.cpolar搭建隧道5.公网ip地址访问您的分享相册6.移动端app使用公网上传照片并及时分享 前言 很多…