labweek8

article/2025/7/1 19:58:44

实验报告

实验内容

 进程间通信—消息机制。

(1) 编译运行课件 Lecture 09 例程代码:Algorithms 9-1 ~ 9-2.

(2) 修改代码,观察在 msgsnd 和 msgrcv 并发执行情况下消息队列的变化情况。

(3) 仿照 alg.8-4~8-6,编制基于 POSIX API 的进程间消息发送和消息接收例程。

实验环境

 Ubuntu 20.04.2.0(64位)

实验过程

一. alg.9-0 ~ alg.9-2

 (一)对代码中不熟悉的内容进行解析:

  1. msgget()参考资料

函数原型int msgget(key_t key, int msgflg);

函数功能: 用来创建和访问一个消息队列。

参数:

key:IPC key 。

msgflg: 权限标志。

返回值: 成功返回文件标识符,失败返回-1。

  1. fopen()参考资料

函数原型FILE *fopen(char *filename, *type);

函数功能: 用于打开文件。

参数:

FILE: 一个包括了文件管理有关信息的数据结构。

filename: 表示文件名, 可以包含路径和文件名两部分。

type: 表示打开文件的类型。参数:

"r"           打开文字文件只读
"w"           创建文字文件只写
"a"           增补, 如果文件不存在则创建一个
"r+"          打开一个文字文件读/写
"w+"          创建一个文字文件读/写
"a+"          打开或创建一个文件增补
"b"           二进制文件(可以和上面每一项合用)
"t"           文本文件(默认项)"rb" 表示打开文字文件只读的二进制文件

返回值: 成功返回文件指针,失败返回NULL。

  1. msgctl()参考资料

函数原型int msgctl(int msgid, int command, struct msgid_ds *buf);

函数功能: 用来控制消息队列,它与共享内存的shmctl函数相似。

参数:

msgid:消息队列对象的标识符。

command: 是将要采取的动作。它可以取3个值:

IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖 msgid_ds 的值。IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值IPC_RMID:删除消息队列

buf: 指向 msgid_ds 结构的指针,它指向消息队列模式和访问权限的结构。

返回值: 成功返回0,失败返回-1

  1. feof()参考资料

函数原型int feof(FILE *stream);

函数功能: 检测流上的文件结束符的函数。

参数:

stream:指向 FILE 对象的指针,该 FILE 对象标识了流。

返回值: 当检测到与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。

  1. fscanf参考资料

函数原型int fscanf(FILE *stream, const char *format, ...);

函数功能: 从流 stream 读取格式化输入。

返回值:

stream: 指向 FILE 对象的指针,该 FILE 对象标识了流。

format: C 字符串。

例:

代码中
fscanf(fp, "%ld %s", &msg_type, buffer);第一次循环:
msg_type = 1
buffer = "Luffy"
  1. msgsnd()参考资料

函数原型int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);

函数功能: 用来把消息添加到消息队列中。

参数:

msgid:消息队列对象的标识符。

msg_ptr: 一个指向准备发送消息的指针。

msg_sz: msg_ptr指向的消息的长度。

msgflg: 控制函数行为的标志。

0: 阻塞方式IPC_NOWAIT:非阻塞方式

返回值: 消息数据的一份副本将被放到消息队列中,并返回0,失败时返回-1。

  1. fclose()参考资料

函数原型int fclose(FILE *stream);

函数功能: 关闭流 stream,刷新所有的缓冲区。

参数:

stream: 指向 FILE 对象的指针,该 FILE 对象指定了要被关闭的流。

返回值: 如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。

  1. msgrcv()参考资料

函数原型int msgrcv(int msgid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

函数功能: 用来从一个消息队列获取消息。

参数:

msgid:消息队列对象的标识符。

msg_ptr: 一个指向准备发送消息的指针。

msg_sz: msg_ptr指向的消息的长度。

msgtype: 可以实现一种简单的接收优先级。如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。

msgflg: 控制函数行为的标志。

0: 阻塞方式IPC_NOWAIT:非阻塞方式

返回值: 调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1。

 (二)运行

  1. 查看本机的消息队列大小限制
    在这里插入图片描述

  2. 运行alg.9-1
    在这里插入图片描述
    创建了一个名为 msg 消息队列,一开始队列中没有消息,有32个空位。将 alg.9-0-msgsnd.txt 中的信息逐个加入到队列中,完成后队列中有10个消息,用掉了10个空位,即用掉了5120bytes。

  3. 运行alg.9-2
    在这里插入图片描述
    msgtype = 0: 输出消息队列中的第一个消息,所以循环之后,就是按顺序输出消息队列中的元素。

    然后不删除消息队列。
    在这里插入图片描述
    观察到此时消息队列中的消息数为0。

    通过 ipcrm -q 删除 msqid = 6 的消息队列(即此队列)。
    在这里插入图片描述
    消息队列已被删除。

  4. 改变传入的参数再次运行 alg.9-2
    在这里插入图片描述
    在这里插入图片描述
    此时 msgtype = 1,通过循环输出消息队列中标号为1的消息。进程 alg.9-2 接收到了3个消息,消息队列中还有7个消息。

    继续改变 msgtype。
    在这里插入图片描述
    消息队列中没有标号为7的消息,所以进程没有接收到消息。

    继续改变 msgtype。
    在这里插入图片描述
    进程接收到标号为4的两个消息,消息队列中只剩下5个消息。

    将队列中剩余的消息全部输出。
    在这里插入图片描述
    将消息队列删除。
    在这里插入图片描述

二. 修改代码,观察在 msgsnd 和 msgrcv 并发执行情况下消息队列的变化情况。

(一) 修改后的代码

  1. alg.9-1-msgsnd.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/msg.h>#include <sys/stat.h>#include <fcntl.h>#include "alg.9-0-msgdata.h"int main(int argc, char *argv[]){char pathname[80];struct stat fileattr;key_t key;struct msg_struct data;long int msg_type;char buffer[TEXT_SIZE];int msqid, ret, count = 0;FILE *fp;if(argc < 2) {printf("Usage: ./a.out pathname\n");return EXIT_FAILURE;}strcpy(pathname, argv[1]);if(stat(pathname, &fileattr) == -1) {ret = creat(pathname, O_RDWR);if (ret == -1) {ERR_EXIT("creat()");}printf("shared file object created\n");}key = ftok(pathname, 0x27); /* project_id can be any nonzero integer */if(key < 0) {ERR_EXIT("ftok()");}printf("\nIPC key = 0x%x\n", key);	msqid = msgget((key_t)key, 0666 | IPC_CREAT);if(msqid == -1) {ERR_EXIT("msgget()");}struct msqid_ds msqattr;ret = msgctl(msqid, IPC_STAT, &msqattr);printf("number of messages remainded = %ld, empty slots = %ld\n", msqattr.msg_qnum, 16384/TEXT_SIZE-msqattr.msg_qnum);printf("NonBlocking Sending ... \n");while (1) {printf("Enter the number of msgtype and name(when msgtype < 0, quit):\n");scanf("%ld", &msg_type);if(msg_type < 0){break;}getchar();fgets(buffer, TEXT_SIZE, stdin);data.msg_type = msg_type;strcpy(data.mtext, buffer);ret = msgsnd(msqid, (void *)&data, TEXT_SIZE, 0); /* 0: blocking send, waiting when msg queue is full */if(ret == -1) {ERR_EXIT("msgsnd()");}count++;printf("number of sent messages = %d\n", count);printf("\n");}system("ipcs -q");exit(EXIT_SUCCESS);}
  1. alg.9-2-msgrcv.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/msg.h>#include <sys/stat.h>#include "alg.9-0-msgdata.h" int main(int argc, char *argv[]) /* Usage: ./b.out pathname msg_type */{key_t key;struct stat fileattr;char pathname[80];int msqid, ret, count = 0, sum = 0;struct msg_struct data;long int msgtype = 0;   /* 0 - type of any messages */if(argc < 2) {printf("Usage: ./b.out pathname msg_type\n");return EXIT_FAILURE;}strcpy(pathname, argv[1]);if(stat(pathname, &fileattr) == -1) {ERR_EXIT("shared file object stat error");}if((key = ftok(pathname, 0x27)) < 0) {ERR_EXIT("ftok()");}printf("\nIPC key = 0x%x\n", key);msqid = msgget((key_t)key, 0666); /* do not create a new msg queue */if(msqid == -1) {ERR_EXIT("msgget()");}struct msqid_ds msqattr;ret = msgctl(msqid, IPC_STAT, &msqattr);while (1) {printf("Enter the number of msgtype(when msgtype < 0, quit):\n");scanf("%ld", &msgtype);getchar();if(msgtype < 0){break;}while(1){ret = msgrcv(msqid, (void *)&data, TEXT_SIZE, msgtype, IPC_NOWAIT); /* Non_blocking receive */if(ret == -1) { /* end of this msgtype */printf("number of received messages = %d\n", count);break;}printf("%ld %s\n", data.msg_type, data.mtext);count++;}sum += count;count = 0;ret = msgctl(msqid, IPC_STAT, &msqattr);printf("number of messages remainding = %ld\n", msqattr.msg_qnum); system("ipcs -q");}printf("number of messages remainding = %ld\n", msqattr.msg_qnum); printf("sum of received messages = %d\n", sum);if(msqattr.msg_qnum == 0) {printf("do you want to delete this msg queue?(y/n)\n");if(getchar() == 'y') {if(msgctl(msqid, IPC_RMID, 0) == -1)perror("msgctl(IPC_RMID)");}}system("ipcs -q");exit(EXIT_SUCCESS);
}

(二) 运行并观察

  1. 多终端同时运行 alg.9-1 和 alg.9-2。
    在这里插入图片描述
    在这里插入图片描述

  2. 先试试接收(通过 alg.9-2)。
    在这里插入图片描述
    此时消息队列里没有消息,所以接收不到消息。

  3. 发送3条消息(通过 alg.9-1)。
    在这里插入图片描述

  4. 在查看消息队列的变化(通过 alg.9-2)。
    在这里插入图片描述
    此时队列中有3条消息,与前面的输入相符。

  5. 接收消息(通过 alg.9-2)。
    在这里插入图片描述

  6. 发送一条消息后退出(通过 alg.9-1)。
    在这里插入图片描述

  7. 直接退出并且不删除消息队列(通过 alg.9-2)。
    在这里插入图片描述
    从显示看,最后消息队列还在,且队列中还有一条消息。

  8. 删除消息队列。
    在这里插入图片描述

三. 仿照 alg.8-4~8-6,编制基于 POSIX API 的进程间消息发送和消息接收例程。

(一)对代码中不熟悉的内容进行解析:

  1. mq_open()参考资料

函数原型mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr );

函数功能: 用来创建和访问一个消息队列。

参数:

name:表示消息队列的名字,它符合POSIX IPC的名字规则。

oflag:表示打开的方式,和open函数的类似。有必选的选项:O_RDONLY,O_WRONLY,O_RDWR,还有可选的选项:O_NONBLOCK,O_CREAT,O_EXCL。

mode:是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时,才需要提供该参数。表示默认访问权限。可以参考open。

attr:也是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时才需要。该参数用于给新队列设定某些属性,如果是空指针,那么就采用默认属性。

返回值: 成功返回消息队列描述符,失败返回-1。

  1. mq_close()参考资料

函数原型mqd_t mq_close(mqd_t mqdes);

函数功能: 用来关闭一个消息队列。

参数:

mqdes: 消息队列的描述符。

返回值: 成功返回0,失败返回-1。

  1. mq_unlink()参考资料

函数原型mqd_t mq_unlink(const char *name);

函数功能: 用来删除一个消息队列。

参数:

name: 指向消息队列名字的指针,名字的形式是 “/somename”。

返回值: 成功返回0,失败返回-1。

  1. mq_send()参考资料

函数原型mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);

函数功能: 向消息队列中写入一条消息。

参数:

mqdes:消息队列描述符;

msg_ptr:指向消息结构体体缓冲区的指针;

msg_len:消息体的长度。

msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send是置msg_prio为0,mq_receive的msg_prio置为NULL。

返回值: 成功返回0,失败返回-1。

  1. mq_receive()参考资料

函数原型mqd_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);

函数功能: 从消息队列中获取消息。

参数:

mqdes:消息队列描述符;

msg_ptr:指向消息结构体缓冲区的指针;

msg_len:消息体的长度,一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。

msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send是置msg_prio为0,mq_receive的msg_prio置为NULL。

返回值: 成功返回0,失败返回-1。

  1. mq_getattr()参考资料

函数原型int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);

函数功能: 获取消息队列的属性,取得的结果存放在参数 mqstat 指针指向的内存。

参数:

mqdes:消息队列描述符;

mqstat:指向消息队列属性结构体的指针

struct mq_attr {long    mq_flags //消息队列的标志:0或O_NONBLOCK,用来表示是否阻塞 long    mq_maxmsg  //消息队列的最大消息数long    mq_msgsize  //消息队列中每个消息的最大字节数long    mq_curmsgs  //消息队列中当前的消息数目
};

返回值: 成功返回0,失败返回-1。

(二) 代码

  1. 发送单个固定信息

alg.9-3-mqctr.c

/* gcc -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <mqueue.h>#include "alg.9-0-msgdata.h"int main(int argc, char *argv[])
{int ret;pid_t childpid1, childpid2;printf("Control:\n");mqd_t mqID;mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT, 0666, NULL);if(mqID == -1){ERR_EXIT("mq_open()");}printf("\nmqID = %d\n", mqID);struct mq_attr mqAttr;mq_getattr(mqID, &mqAttr);printf("number of messages remainded = %ld, empty slots = %ld\n", mqAttr.mq_curmsgs, 16384 / TEXT_SIZE - mqAttr.mq_curmsgs);printf("\n");system("ls -l /dev/mqueue/");   printf("\n");char *argv1[] = {" ", argv[1], 0};childpid1 = vfork();if(childpid1 < 0) {ERR_EXIT("mqctr: 1st vfork()");} else if(childpid1 == 0) {execv("./alg.9-4-mqsnd.o", argv1); /* call sender with filename */}else {childpid2 = vfork();if(childpid2 < 0) {ERR_EXIT("mqctr: 2nd vfork()");}else if (childpid2 == 0) {execv("./alg.9-5-mqrcv.o", argv1); /* call receiver with filename */}else {wait(&childpid1);wait(&childpid2);printf("Control:\n\n");ret = mq_getattr(mqID, &mqAttr);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs); if(mqAttr.mq_curmsgs == 0) {printf("do you want to delete this msg queue?(y/n)\n");if(getchar() == 'y') {if(mq_unlink("/anonymQueue") == -1){ERR_EXIT("mq_unlink()");} }}system("ls -l /dev/mqueue/");   }}exit(EXIT_SUCCESS);
}

alg.9-4-mqsnd.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <unistd.h>
#include <fcntl.h>#include "alg.9-0-msgdata.h"int main(int argc, char *argv[])
{char buffer[TEXT_SIZE] = "POSIX message queue test.";int ret;printf("Sender:\n");mqd_t mqID;mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT, 0666, NULL);if(mqID == -1){ERR_EXIT("mq_open");}printf("\nmqID = %d\n", mqID);struct mq_attr mqAttr;mq_getattr(mqID, &mqAttr);printf("number of messages remainded = %ld, empty slots = %ld\n", mqAttr.mq_curmsgs, 16384 / TEXT_SIZE - mqAttr.mq_curmsgs);printf("Max msgsize = %ld\n", mqAttr.mq_msgsize);printf("Sending ... \n");ret = mq_send(mqID, buffer, TEXT_SIZE, 0); if(ret == -1) {ERR_EXIT("mq_send()");}ret = mq_getattr(mqID, &mqAttr);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("Send message: %s\n", buffer);printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs); system("ls -l /dev/mqueue/"); printf("\n");  if(mq_close(mqID) == -1){ERR_EXIT("mq_close()");} exit(EXIT_SUCCESS);
}

alg.9-5-mqrcv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mqueue.h>#include "alg.9-0-msgdata.h" int main(int argc, char *argv[])
{char buffer[TEXT_SIZE];int ret;sleep(1);printf("Receiver:\n");mqd_t mqID;mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT, 0666, NULL);if(mqID == -1){ERR_EXIT("mq_open");}printf("\nmqID = %d\n", mqID);struct mq_attr mqAttr;ret = mq_getattr(mqID, &mqAttr);printf("Max msgsize = %ld\n", mqAttr.mq_msgsize);ret = mq_receive(mqID, buffer, mqAttr.mq_msgsize, NULL);if(ret == -1) { ERR_EXIT("mq_reveive()");}printf("Receive message: %s\n",buffer);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs); system("ls -l /dev/mqueue/");   printf("\n");if(mq_close(mqID) == -1){ERR_EXIT("mq_close()");} exit(EXIT_SUCCESS);
}

运行
在这里插入图片描述
mqctr 先创建了消息队列 anonymQueue,此时队列为空。

然后 mqsnd 连接到消息队列,发送了一条消息到消息队列中,可以看到此时队列中有一条消息。发送完毕后 mqsnd 关闭消息队列并结束进程。

mqrcv 连接到消息队列中并从消息队列中将 mqsnd 发送的那条消息接收并打印出来。结束进程。

两个子进程结束后父进程 mqctr 继续执行。此时消息队列为空。

不删除消息队列,结束父进程。
在这里插入图片描述
再运行一次。这次删除消息队列。

可以看到消息队列已被删除。

  1. 循环发送固定信息。
    在这里插入图片描述
    在这里插入图片描述
  2. 并发执行

alg.9-4-mqsnd.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <unistd.h>
#include <fcntl.h>#include "alg.9-0-msgdata.h"int main(int argc, char *argv[])
{char buffer[TEXT_SIZE];int ret, count = 0;int flag;mqd_t mqID;mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT, 0666, NULL);if(mqID == -1){ERR_EXIT("mq_open");}printf("\nmqID = %d\n", mqID);struct mq_attr mqAttr;mq_getattr(mqID, &mqAttr);printf("number of messages remainded = %ld, empty slots = %ld\n", mqAttr.mq_curmsgs, 16384 / TEXT_SIZE - mqAttr.mq_curmsgs);printf("Max msgsize = %ld\n", mqAttr.mq_msgsize);printf("Sending ... \n");while(1){printf("Enter flag(1: send;  0: quit):\n");scanf("%d", &flag);getchar();if(flag == 0){break;}printf("Enter message:\n");fgets(buffer, TEXT_SIZE, stdin);ret = mq_send(mqID, buffer, TEXT_SIZE, 0); if(ret == -1) {ERR_EXIT("mq_send()");}ret = mq_getattr(mqID, &mqAttr);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("Send message: %s\n", buffer);printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs); system("ls -l /dev/mqueue/"); printf("\n");  }ret = mq_getattr(mqID, &mqAttr);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs); printf("do you want to delete this msg queue?(y/n)\n");if(getchar() == 'y') {if(mq_unlink("/anonymQueue") == -1){ERR_EXIT("mq_unlink()");} }else{if(mq_close(mqID) == -1){ERR_EXIT("mq_close()");}}system("ls -l /dev/mqueue/"); exit(EXIT_SUCCESS);
}

alg.9-5-mqrcv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mqueue.h>#include "alg.9-0-msgdata.h" int main(int argc, char *argv[])
{char buffer[TEXT_SIZE];int ret, count = 0;int flag;mqd_t mqID;mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT, 0666, NULL);if(mqID == -1){ERR_EXIT("mq_open");}printf("\nmqID = %d\n", mqID);struct mq_attr mqAttr;ret = mq_getattr(mqID, &mqAttr);printf("Max msgsize = %ld\n", mqAttr.mq_msgsize);while(1){printf("Enter flag(1: receive;  0: quit):\n");scanf("%d", &flag);getchar();if(flag == 0){break;}ret = mq_receive(mqID, buffer, mqAttr.mq_msgsize, NULL);if(ret == -1) { ERR_EXIT("mq_reveive()");}printf("Receive message: %s\n",buffer);ret = mq_getattr(mqID, &mqAttr);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs);system("ls -l /dev/mqueue/");   printf("\n");}ret = mq_getattr(mqID, &mqAttr);if(ret == -1){ERR_EXIT("mq_getattr()");}printf("number of messages remainding = %ld\n", mqAttr.mq_curmsgs); system("ls -l /dev/mqueue/"); if(mq_close(mqID) == -1){ERR_EXIT("mq_close()");} exit(EXIT_SUCCESS);
}

运行
(1)两个程序并发运行
在这里插入图片描述
在这里插入图片描述
(2)发送消息
在这里插入图片描述

发送3条手动输入的消息,发送成功,可以看到队列中有3条消息。

(3)接收消息
在这里插入图片描述

手动接收了2条消息,消息内容与发送的一致。

(4)退出

mqrcv
在这里插入图片描述
mqsnd
在这里插入图片描述
都显示此时队列中还有一条消息。不删除消息队列。

(5)再运行一次
在这里插入图片描述
输出消息队列中剩下的消息。

在这里插入图片描述
删除队列。


http://chatgpt.dhexx.cn/article/LdX18yRZ.shtml

相关文章

常见垃圾回收器

CMS和G1是最重要的 新生代一般采用标记复制&#xff0c;老年代一般采用标记整理算法 Serial&#xff1a;垃圾回收线程只有一个&#xff0c;而且垃圾回收线程工作的时候其他用户线程要停下来 Parnew&#xff1a;Serial的多线程版本&#xff0c;有多个垃圾回收线程&#xff0c;垃…

垃圾回收(一)

文章目录 1. 确定哪些对象还“存活”&#xff0c;哪些已经“死去”1.1 引用计数算法1.2 可达性分析算法1.3 引用 2.垃圾回收算法2.1 分代收集理论跨代引用 2.2 标记-清除算法2.3 标记-复制法2.4 标记-整理算法 垃圾收集需要完成的三件事&#xff1a; 哪些内存需要回收&#xff…

python 垃圾箱-垃圾回收

前戏 解释器在执行到定义变量的语法时&#xff0c;会申请内存空间来存放变量的值&#xff0c;而内存的容量是有限的&#xff0c;这就涉及到变量值所占用内存空间的回收问题&#xff0c;当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉&#xff0c;那什么样的变量值…

纪念第一届cccc天梯赛

来一篇迟到的文章 先来爆一下战绩吧&#xff1a;SDUT&#xff0c;学校排名全国第六获金&#xff0c;山东省冠军。学校一共三支队伍&#xff0c;学长们一支最强的队获一等奖&#xff0c;剩下的两支二等奖。 战绩总体来看还不错吧&#xff0c;离不开我们每一个队员和老师们的努…

2021 CCCC天梯赛补题

前言不想看请直接跳过&#xff5e;&#xff5e;&#xff5e;&#xff5e; 前言检讨&#xff1a;天梯赛我拉垮了&#xff0c;我拖了队伍后腿&#xff0c;我有罪。 分析原因&#xff0c;首先是前一个星期训练量不够&#xff0c;没有跟上队友训练的进度&#xff0c;一些基础的STL的…

2021年CCCC天梯赛 【部分题题解】

天梯赛有三个level&#xff0c;第一个level基本就是语法题&#xff0c;第二个level是基础算法和STL库的一些应用。 第三个level就是一些难的算法。 L3的题都不是太会&#xff0c;有思路但是写不出来。 目录 L1人与神两小时学完C语言强迫症降价提醒机器人大笨钟的心情吉老师的回…

2021年CCCC天梯赛L3 还原文件题解

题目如下 一份重要文件被撕成两半&#xff0c;其中一半还被送进了碎纸机。 我们将碎纸机里找到的纸条进行编号&#xff0c;如图 1 所示。然后根据断口的折线形状跟没有切碎的半张纸进行匹配&#xff0c;最后还原成图 2 的样子。 要求你输出还原后纸条的正确拼接顺序。 图 1…

2020CCCC天梯赛补题记录

目录 总结补题L2-035 完全二叉树的层序遍历 (25分)L2-036 网红点打卡攻略 (25分)L3-025 那就别担心了 (30分)28分版本&#xff1a;30分版本(记忆化搜索) 总结 1.口罩那题打完就只剩三十分钟了&#xff0c;之后卡在了完全二叉树的层序遍历那题&#xff0c;就没有再敢往后看&…

2021 CCCC天梯赛L1补题

前言 这次天梯赛表现没有达到预期&#xff0c;L1的分没有拿全&#xff0c;L2有2题没考虑完全&#xff0c;总之很愧疚拖了队友后腿。今天先补上L1没拿满分的题。 L1补题 前言L1-078 吉老师的回归输入样例1输出样例1输入样例2输出样例2 思路代码实现 L1-080 乘法口诀数列输入样例…

CCCC天梯赛 L2-037 包装机

CCCC天梯赛 L2-037 包装机 一种自动包装机的结构如图 1 所示。 首先机器中有 N 条轨道&#xff0c;放置了一些物品。 轨道下面有一个筐。 当某条轨道的按钮被按下时&#xff0c;活塞向左推动&#xff0c;将轨道尽头的一件物品推落筐中。 当 0 号按钮被按下时&#xff0c;机械手…

2022年CCCC天梯赛题解

L1-1今天我要赢 原题链接 代码 #include<bits/stdc.h> #define int long long #define rep(i, a, b) for(int ia;i<b;i) #define Rep(i, a, b) for(int ia;i>b;--i) using namespace std; const int N 10005; inline int read(){int s0,w1;char chgetchar();wh…

第八届cccc团体程序设计天梯赛——个人参赛总结——无代码纯粹的参赛总结

第八届cccc团体程序设计天梯赛——个人参赛总结——无代码纯粹的参赛总结 目录 第八届cccc团体程序设计天梯赛——个人参赛总结——无代码纯粹的参赛总结引言~介绍一下cccc天梯赛&#xff08;选读&#xff09;开篇介绍&#xff08;以下是个人经历部分的分享&#xff09;赛前准备…

怎样合理地营销推广App和吸引住大量的客户提高转化?

开发App进行后&#xff0c;一切应用软件都必须营销推广&#xff0c;尤其是在互联网技术收益消退的情况下&#xff0c;怎样合理地营销推广和吸引住大量的客户总流量早已变成很多互联网公司最关注的难题。 APP运用引流方法工作中看上去很繁杂&#xff0c;难度系数很大&#xff0…

APP在应用市场内该如何做推广

苹果应用商城的自然流量都是通过精品推荐&#xff0c;畅销排行榜和搜索来获取的&#xff0c;此外&#xff0c;应用名称、副标题、应用截图视频、应用描述、用户评论、下载量、用户留存率还有曝光量&#xff0c;这些都是影响ASO优化的关键因素。 为了防止一些应用堆砌热词&…

App推广拉新的6大方式,你都玩得转吗?

中国的互联网发展至今,除了App以外,公众号,小程序在微信生态中的独领风骚,快应用也像“太子”一样在11家国产手机厂商的簇拥下茁壮成长,而百度的百家号、头条的头条号、阿里的大鱼号也已经和微信开始“正面刚”。App这种古老的产品形态,正受到市场的不断冲击。 根据Quest…

什么是App推广技术?

在移动互联网红利消失殆尽、市场竞争日趋激烈的背景下,App的推广越来越难了,如何去有效的进行推广,吸引更多的用户流量,成为了众多互联网企业最为关注的问题。 而App 推广技术指的就是通过一些技术的方式来提高 App 推广效率,帮助众多互联网企业,解决 App 推广难的问题。…

如何正确推广App,实现高转化用户。

个性化深度链接技术的应用。深度链接使网页和App两者串联起来&#xff0c;减少了转换漏斗中的损失。再加上深度链接不仅能改善潜在用户的转化&#xff0c;对沉睡用户的唤醒同样也能发挥关键作用。 类型不含深度链接使用深度链接携带参数首页商品的id号&#xff0c;房间号&…

电商App:提高推广转化率的几件事

双十一将至&#xff0c;电商App获客却越来越难&#xff0c;成本越来越高。想要做好用户拉新并转化为有效用户&#xff0c;这里整理了一套拉新留存方法论&#xff0c;可以参考使用。 一、找准目标用户 首先明确用户群定位&#xff0c;可以从年龄范围、职业范围、性别、地域、消…

微信开放平台unionID 的利用(app推广转化率的解决方案)

打开微信公众号开发文档&#xff0c;开幕就是这段话。微信开放平台提供了unionID 一个用户虽然对多个公众号和应用有多个不同的OpenID&#xff0c;但他对所有这些同一开放平台账号下的公众号和应用&#xff0c;只有一个UnionID 正常的app推广方案有很多平台在做&#xff0c;费…

小程序手写板电子签名

本人已经全部处理好&#xff0c;可以直接粘贴文件使用不予要修改&#xff0c;可保存图片到本地&#xff0c;也可以转成base64位码。源码地址&#xff1a;https://download.csdn.net/download/zzsjsp/11816038