《嵌入式系统开发实践》实验三 进程通信

news2024/11/25 15:24:18

一、 实验目的

  1. 掌握无名管道与有名管道的进程通信;
  2. 掌握消息队列的读写操作;
  3. 掌握共享内存的通信机制。

二、 实验任务与要求

  1. 管道读写程序的编写与应用;
  2. 消息队列的发送和接收程序的编写和应用;
  3. 共享内存的创建、连接和分离编程和应用。

三、 实验工具和环境
PC机、Linux Ubuntu操作系统。
四、 实验内容与结果

  1. 利用无名管道通信编写程序实现命令cat的功能。
    7.2.1-1
    在这里插入图片描述
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    // printf("%d", argc);
    if (argc < 2)
    {
        printf("argv lost\n");
        exit(0);
    }
    int fd[2];
    int err = pipe(fd);
    if (err == -1)
    {
        printf("pipe err\n");
        exit(0);
    }
    pid_t pid = fork();
    if (pid == -1)
        exit(0);
    else if (pid == 0)
    {
        close(fd[1]);
        char buf[256] = {0};
        int size = read(fd[0], buf, 256);

        if (size > 0)
            printf("son --- %s\n", buf);
        else
            printf("son read err\n");

        close(fd[0]);
        exit(0);
    }
    else if (pid > 0)
    {
        close(fd[0]);
        int fd2, size2;
        char buf2[256];
        fd2 = open(argv[1], O_RDONLY);
        if (fd2)
        {
            size2 = read(fd2, buf2, 256);
            write(fd[1], buf2, 256);
        }
        close(fd2);
        sleep(5);
        close(fd[1]);
        wait(NULL);
        exit(0);
    }
}

在这里插入图片描述
这段代码实现了一个简单的管道通信,父进程通过读取文件内容,将数据写入管道,子进程从管道中读取数据并打印。其中使用了fork创建子进程,pipe创建管道,open函数打开文件,read和write函数进行读写操作。程序在父进程中使用wait函数等待子进程退出。

  1. 设计两个程序:有名管道的读程序和写程序,要求利用有名管道实现聊天程序,每次发言后自动在后面增加当前系统时间。增加结束字符,比如最后输入“886”后结束进程。

写程序

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

int main(void)
{
    int fd;
    int len;
    char buf[PIPE_BUF];
    time_t tp;
    printf("I am %d\n", getpid());
    if ((fd = open("fifo1", O_RDWR)) < 0)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        time(&tp);
        printf("\n[%d]请输入文字:", getpid());
        char text[256];
        fgets(text, (sizeof text / sizeof text[0]), stdin);
        if (strcmp(text, "886\n") == 0)
        {
            printf("\n886!\n");
            close(fd);
            exit(EXIT_SUCCESS);
        }
        len = sprintf(buf, "-[%d]: %s%s", getpid(), text, ctime(&tp));
        if ((write(fd, buf, len)) < 0)
        {
            perror("write");
            close(fd);
            exit(EXIT_FAILURE);
        }
        sleep(3);
    }
    close(fd);
    exit(EXIT_SUCCESS);
}

读程序

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>

int main(void)
{
    int fd;
    int len;
    char buf[PIPE_BUF];
    mode_t mode = 0666;
    system("rm fifo1 > null");
    if ((mkfifo("fifo1", mode)) < 0)
    {
        perror("mkfifo");
        exit(EXIT_FAILURE);
    }
    if ((fd = open("fifo1", O_RDONLY)) < 0)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }

    while ((len = read(fd, buf, PIPE_BUF)) > 0)
    {
        printf("%s", buf);
    }
    close(fd);
    exit(EXIT_SUCCESS);
}

在这里插入图片描述

这两段代码是一个进程间通信的例子,使用了命名管道(FIFO)来实现。第一个程序是写程序,不断从命令行读取用户输入的文字,将其和当前时间一起发送到命名管道中。第二个程序是读程序,不断从命名管道中读取数据并输出到控制台。通过命名管道,实现了两个进程之间的通信。其中,mkfifo函数用于创建命名管道,open函数用于打开命名管道,read和write函数用于读写数据,close函数用于关闭文件描述符。

  1. 设计一个程序,要求用函数msgget创建消息队列,从键盘输入的字符串添加到消息队列,然后应用函数msgrcv读取队列中的消息并在计算机屏幕上输出。程序先调用msgget函数创建、打开消息队列,接着调用msgsnd函数,把输入的字符串添加到消息队列中,然后调用msgrcv函数,读取消息队列中的消息并打印输出,最后调用msgctl函数,删除系统内核中的消息队列。
// q3reader.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st
{

	long int my_msg_type;
	char some_text[BUFSIZ];
};
int main(void)
{

	int running = 1;
	int msgid;
	struct my_msg_st some_data; // 定义消息变量
	long int msg_to_receive = 0;
	msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
	if (msgid == -1)
	{
		fprintf(stderr, "msgget failed with error: %d\n", errno);
		exit(EXIT_FAILURE);
	}
	/*创建消息队列*/
	/*循环从消息队列中接收消息*/
	while (running)
	{
		/*读取消息*/
		if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
		{
			fprintf(stderr, "msgrcv failed with error: %d\n", errno);
			exit(EXIT_FAILURE);
		}
		printf("You wrote: %s", some_data.some_text);
		/*接收到的消息为“end”时结束循环*/
		if (strncmp(some_data.some_text, "end", 3) == 0)
		{
			running = 0;
		}
	}
	/*从系统内核中移走消息队列*/
	if (msgctl(msgid, IPC_RMID, 0) == -1)
	{
		fprintf(stderr, "msgctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}
	exit(EXIT_SUCCESS);
}

// q3writer.c 
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st
{

	long int my_msg_type;
	char some_text[MAX_TEXT];
};
int main(void)
{
	int running = 1;
	struct my_msg_st some_data;
	int msgid;
	char buffer[BUFSIZ];
	/*创建消息队列*/
	msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
	if (msgid == -1)
	{
		fprintf(stderr, "msgget failed with error:%d\n", errno);
		exit(EXIT_FAILURE);
	}
	while (running)
	{ /*循环向消息队列中添加消息*/
		printf("Enter some text:");
		fgets(buffer, BUFSIZ, stdin); // 从标准输入文件读取字符串赋给buffer
		some_data.my_msg_type = 1;
		strcpy(some_data.some_text, buffer); // buffer的内容复制给消息
		/*添加消息*/
		if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1)
		{
			fprintf(stderr, "msgsed failed\n");
			exit(EXIT_FAILURE);
		}
		/*用户输入的为“end”时结束循环*/
		if (strncmp(buffer, "end", 3) == 0)
		{
			running = 0;
		}
	}
	exit(EXIT_SUCCESS);
}

在这里插入图片描述

这两段代码分别实现了消息队列的读和写操作。其中,q3reader.c创建了一个消息队列,并通过循环从中接收消息,如果接收到的消息为“end”,则结束程序;而q3writer.c循环向消息队列中添加消息,如果用户输入的消息为“end”,则结束程序。两段代码都使用了结构体my_msg_st来定义消息,其包含了消息类型my_msg_type和消息内容some_text。在创建/添加消息的时候,需要使用msgsnd/mssgrcv函数,并将my_msg_st作为参数传递进去。同时,需要使用msgget函数获取消息队列的ID,并使用msgctl函数移走消息队列。

  1. 设计两个程序要求用消息队列实现简单的聊天功能。
// q3reader.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st
{

	long int my_msg_type;
	char some_text[BUFSIZ];
};
int main(void)
{

	int running = 1;
	int msgid;
	struct my_msg_st some_data; // 定义消息变量
	long int msg_to_receive = 0;
	msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
	if (msgid == -1)
	{
		fprintf(stderr, "msgget failed with error: %d\n", errno);
		exit(EXIT_FAILURE);
	}
	/*创建消息队列*/
	/*循环从消息队列中接收消息*/
	while (running)
	{
		/*读取消息*/
		if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
		{
			fprintf(stderr, "msgrcv failed with error: %d\n", errno);
			exit(EXIT_FAILURE);
		}
		printf("You wrote: %s", some_data.some_text);
		/*接收到的消息为“end”时结束循环*/
		if (strncmp(some_data.some_text, "end", 3) == 0)
		{
			running = 0;
		}
	}
	/*从系统内核中移走消息队列*/
	if (msgctl(msgid, IPC_RMID, 0) == -1)
	{
		fprintf(stderr, "msgctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}
	exit(EXIT_SUCCESS);
}

// q3writer.c 
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st
{

	long int my_msg_type;
	char some_text[MAX_TEXT];
};
int main(void)
{
	int running = 1;
	struct my_msg_st some_data;
	int msgid;
	char buffer[BUFSIZ];
	/*创建消息队列*/
	msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
	if (msgid == -1)
	{
		fprintf(stderr, "msgget failed with error:%d\n", errno);
		exit(EXIT_FAILURE);
	}
	while (running)
	{ /*循环向消息队列中添加消息*/
		printf("Enter some text:");
		fgets(buffer, BUFSIZ, stdin); // 从标准输入文件读取字符串赋给buffer
		some_data.my_msg_type = 1;
		strcpy(some_data.some_text, buffer); // buffer的内容复制给消息
		/*添加消息*/
		if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1)
		{
			fprintf(stderr, "msgsed failed\n");
			exit(EXIT_FAILURE);
		}
		/*用户输入的为“end”时结束循环*/
		if (strncmp(buffer, "end", 3) == 0)
		{
			running = 0;
		}
	}
	exit(EXIT_SUCCESS);
}

  1. 在主程序中先调用shmget函数创建一个共享内存,得到共享内存的id,然后利用shmat函数将创建的共享内存连接到一个进程的地址空间,返回值为该内存空间的地址指针,利用地址指针对共享内存进行访问;最后利用shmdt函数分离进程和共享内存。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define BUFSZ 4096
int main(int argc, char *argv[])
{
    int shm_id;
    char *shm_buf;
    key_t key;
    system("touch shmfile");
    // 生成一个共享内存段的唯一键
    key = ftok("shmfile", 65);
    if (key == -1)
    {
        perror("ftok");
        exit(1);
    }
    // 用键和共享内存段的大小创建一个共享内存段并获取它的 ID
    shm_id = shmget(key, BUFSZ, 0666 | IPC_CREAT);
    if (shm_id < 0)
    {
        perror("shmget");
        exit(1);
    }
    printf("successfully created segment: %d \n", shm_id);
    // 将共享内存段连接到进程地址空间并获取一个指向它的指针
    if ((shm_buf = shmat(shm_id, NULL, 0)) == (char *)-1)
    {
        perror("shmat");
        exit(1);
    }
    printf("segment attached at %p\n", shm_buf);
    system("ipcs -m");
    sleep(3); /*休眠*/
    // 将共享内存段从进程地址空间分离
    if ((shmdt(shm_buf)) < 0)
    {
        perror("shmdt");
        exit(1);
    }
    printf("segment detached \n");
    system("ipcs -m "); /*再次查看系统IPC状态*/
    // 删除共享内存段
    if (shmctl(shm_id, IPC_RMID, NULL) == -1)
    {
        perror("shmctl");
        exit(1);
    }
    printf("segment removed \n");
    system("ipcs -m "); /*再次查看系统IPC状态*/
    exit(0);
}

在这里插入图片描述

这段代码演示了创建、连接、分离、删除共享内存段的过程。首先使用ftok函数生成一个共享内存段的唯一键,然后使用shmget函数创建一个共享内存段并获取它的ID。接着使用shmat函数将共享内存段连接到进程地址空间并获取一个指向它的指针。然后可以使用shm_buf指针来读写共享内存段。当不需要使用共享内存段时,可以使用shmdt函数将它从进程地址空间分离。最后使用shmctl函数删除共享内存段并释放它的系统资源。在代码中还使用了system函数调用ipcs命令来查看系统中的IPC状态。

  1. 编写生产者、消费者程序。

(1) 消费者程序中创建一个共享内存段,并将其中的内容显示出来;

消费者程序:

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

#define SHM_SIZE 1024  // 共享内存大小
#define SHM_KEY 1234   // 共享内存键值

int main()
{
    int shmid;
    char *shmaddr;

    // 连接到已有的共享内存段并获取其地址
    shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    shmaddr = (char *)shmat(shmid, NULL, 0);
    if (shmaddr == (char *)(-1)) {
        perror("shmat");
        exit(1);
    }

    printf("Content of shared memory:\n%s", shmaddr);  // 显示共享内存中的内容

    // 断开共享内存连接
    shmdt(shmaddr);

    return 0;
}

(2) 生产者连接到一个已有的共享内存段,并允许向其中写入数据。

生产者程序:

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

#define SHM_SIZE 1024  // 共享内存大小
#define SHM_KEY 1234   // 共享内存键值

int main()
{
    int shmid;
    char *shmaddr;
    char buffer[256];

    // 创建共享内存段
    shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    // 连接到共享内存段并获取其地址
    shmaddr = (char *)shmat(shmid, NULL, 0);
    if (shmaddr == (char *)(-1)) {
        perror("shmat");
        exit(1);
    }

    while (1) {
        printf("Enter message: ");
        fgets(buffer, sizeof(buffer), stdin);
        strncpy(shmaddr, buffer, SHM_SIZE);  // 写入共享内存
    }
    
    // 断开共享内存连接
    shmdt(shmaddr);
    
    // 删除共享内存段
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

在这里插入图片描述
生产者程序使用 shmget() 函数创建了一个大小为 SHM_SIZE 的共享内存段,并使用 shmat() 函数将其连接到进程的虚拟地址空间,从而获取其地址指针 shmaddr。然后通过 fgets() 函数从标准输入读取字符串,再使用 strncpy() 将其写入共享内存中。这个过程循环执行,直到程序结束。最后使用 shmdt() 断开共享内存连接,使用 shmctl() 删除共享内存段。

消费者程序先使用 shmget() 函数连接到已有的共享内存段,并使用 shmat() 函数将其连接到进程的虚拟地址空间,从而获取其地址指针 shmaddr。然后通过 printf() 打印共享内存中的内容。最后使用 shmdt() 断开共享内存连接。

五、 实验总结

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

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

相关文章

ptp4l测试-LinuxPTP\ptp4l配置与问题排查

目录 一、前言 1.1 什么是ptp4l 1.2 描述 1.3 官网连接 1.4 目的 1.5 平台 二、 平台必要软件 2.1 linux物理机 2.2 imx6ull开发板 三、检查硬件是否支持 3.1 ifconfig查看当前网卡的名称 3.2 ethtool检查驱动和网卡是否支持 四、开启ptp4l服务 4.1 方法一&#x…

Windows远程访问本地 jupyter notebook服务

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 前言 Jupyter Notebook&#xff0c;它是一个交互式的数据科学和计算环境&#xff0c;支持多种编程语言&#…

图像滤波概述

什么是图像滤波 1.图像滤波&#xff0c;即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制&#xff0c;是图像预处理中不可缺少的操作&#xff0c;其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。 2.消除图像中的噪声成分叫作图像的平滑化或滤波操…

霍尔电流传感器在直流列头柜的应用

摘要&#xff1a;数据中心供电电源质量的好坏直接影响到IT设备的安全运行&#xff0c;因此对数据中心直流列头柜电源进出线实行监测非常重要&#xff0c;而通过霍尔电流传感器可以采集主进线电流、多路支路直流电流和漏电流。 关键词&#xff1a;数据中心&#xff1b;直流列头柜…

MySQL-多表查询(下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…

统计:SEM standar deviation of the mean [延长仪表周期 技术点]

平均值标准误差&#xff08;SEM&#xff0c; standard deviation of the mean&#xff09; SD&#xff1a;样本标准差 分母(n-1) SD与SEM区别&#xff1a; 1.SD量化了样本分散值之间的差值有多大&#xff1b; 2.SEM量化了对总体实际平均值的了解程度。其考虑了SD的值和样本量&…

day11 - 手写数字笔迹细化

手写数字笔迹细化 对于手写数字识别实验中&#xff0c;经常会遇到因为笔迹较粗导致误识别的情况&#xff0c;所以我们通常会先将笔迹进行细化&#xff0c;笔迹变细以后&#xff0c;数字的特征会更明显&#xff0c;后续进行识别的准确率就会更高。 例如数字7 和 1 &#xff0c…

2019下半年上午题

2019下半年上午题 b 选a c 最后统一单位 计算需要多少片芯片&#xff1a; 流水线&#xff1a; 也就是&#xff1a; 对于这一道题&#xff1a; c ssl&#xff1a;安全套接层 https&#xff1a;安全通道 PGP&#xff1a;电子邮件加密 d b a b b 受委托方和委…

WWDC2023|苹果已邀请多家VR/AR领域媒体,有望推出首款头显设备

据9to5mac报道&#xff0c;苹果已邀请多位VR/AR领域的记者和创作者参加今年WWDC开发者大会&#xff0c;其中包括UploadVR编辑Ian Hamilton、经常测试AR/VR产品的Norman Chan等&#xff0c;这表明苹果有望推出该领域的相关产品和服务&#xff0c;例如首款VR/AR头显。 根据此前…

动态规划-树形DP

树的重心 题目 链接&#xff1a;https://www.acwing.com/problem/content/848/ 给定一颗树&#xff0c;树中包含 n n n 个结点&#xff08;编号 1 ∼ n 1 \sim n 1∼n&#xff09;和 n − 1 n-1 n−1 条无向边。 请你找到树的重心&#xff0c;并输出将重心删除后&#x…

免费使用GPT4.0?搭载多模态的全新New Bing开放使用教程

目录 1 微软发布新必应2 支持文本生成图像3 支持多模态回答4 历史记录和回答导出5 支持插件化导入 1 微软发布新必应 5月4日&#xff0c;微软基于ChatGPT的搜索引擎New Bing发布了一次大规模更新&#xff0c;并宣布已开放给所有用户&#xff0c;现在无需再排队等待&#xff0c…

实力上榜!ONES 荣获深圳市「专精特新」企业认定

近日&#xff0c;ONES 成功入选 2022 年深圳市专精特新中小企业名单&#xff0c;这标志着 ONES 在研发管理领域专业化、企业数字化程度、质量管理水平、研发创新水平等多个方面得到充分认可和肯定。 深圳市专精特新企业评选是以工业和信息化部、国家统计局、国家发展改革委、财…

Live800:客服系统知识库建设中需要注意的三个要点

互联网的快速发展&#xff0c;让客服行业也随之发生着巨大的变化。传统的客服方式越来越难以满足人们的需求&#xff0c;客户对客服的要求也变得越来越高。在这种情况下&#xff0c;客服系统成为了一种必不可少的工具。 客服系统作为企业与客户沟通的重要渠道&#xff0c;其之所…

聚观早报 | 微软Build开发者大会开幕;阿里云智能裁员7%

今日要闻&#xff1a;微软Build开发者大会开幕&#xff1b;阿里云智能裁员7%&#xff1b;亚马逊中国宣布停止应用商店服务&#xff1b;苹果汽车项目启动已近10年&#xff1b;阿迪达斯中国将重新出售YEEZY系列 微软Build开发者大会开幕 5 月 24 日消息&#xff0c;继两周之前谷…

linux怎么重置密码,CentOS忘记密码,怎么重置密码

1、打开虚拟机 2、在这个界面按 e 进入到这个界面 3、下滑到这个地方&#xff0c;添加这部分代码 init/bin/sh (手动输入&#xff0c;虚拟机算是另一台电脑&#xff0c;复制不过去的) 4、填写完成后按 ctrlx 引导启动 5、输入 mount -o remount, rw / 输入 passwd xxx PS…

Jenkins+GitLab+Docker搭建前端自动化构建镜像容器部署(无本地证书,映射证书)

前言 &#x1f680; 需提前安装环境及知识点&#xff1a; 1、Docker搭建及基础操作 2、DockerFile文件描述 3、Jenkins搭建及基础点 &#x1f680; 目的&#xff1a; 将我们的前端项目打包成一个镜像容器并自动发布部署&#xff0c;可供随时pull访问 一、手动部署镜像及容器 1…

Blazor实战——Known框架增删改查导

本章介绍学习增、删、改、查、导功能如何实现&#xff0c;下面以商品资料作为示例&#xff0c;该业务栏位如下&#xff1a; 类型、编码、名称、规格、单位、库存下限、库存上限、备注 1. 前后端共用 1.1. 创建实体类 在KIMS项目Entities文件夹下创建KmGoods实体类该类继承Ent…

一个程序最多能占用的内存大小

因为内存资源总是稀缺的&#xff0c;即便在拥有百 G 内存的机器上&#xff0c;我们都可以轻易把内存填满。为了解决这个问题&#xff0c;就需要用到虚拟化技术。 GC 是面试的高频重点知识&#xff0c;同时也是程序员日常开发需要理解的部分。学习 GC 有助于你优化你开发应用的性…

1分钟用上ChatGPT,国内用户福音

众所周知的原因&#xff0c;要想在国内使用ChatGPT&#xff0c;肯定是要“折腾一番”的。 但是对于绝大多数普通小白&#xff0c;有没有比较容易的方法就用上官方的ChatGPT呢&#xff1f; 是可以的 最简单的方法就是调用OpenAI官方的API接口 就可以用“曲线救国”的方式用上…

免费部署你的私人 ChatGPT 网页应用

免费部署你的私人 ChatGPT 网页应用 1、注册Github账号&#xff0c;拷贝仓库 第一步、打开GitHub官网&#xff0c;点击右上角Sign up注册即可 第二步、打开开源项目【Chatgpt-next-web】,点击fork&#xff0c;点击Create fork完成操作 2、选择免费的容器【vercel】或者【r…