进程间通讯(IPC机制) 管道 信号量 共性内存 消息队列 详细图解

news2024/11/24 16:32:53

进程间通讯-IPC机制

  • 常用命令
  • 管道
    • 有名管道读写编程
      • 有名管道示意图
    • 无名管道
  • 信号量
    • 信号量的概念
    • 信号量接口函数
    • 进程 a 和进程 b 模拟访问打印机 用信号量互斥
      • 画图分析
      • 代码实现
      • 测试结果
      • 显示和操作 共享内存 信号量 消息队列 的命令
  • 共享内存
    • 共享内存定义
    • 共享内存函数接口
    • 实例编程
      • 图示理解
      • 编码实现
      • 测试结构
  • 消息队列
    • 图示理解
    • 接口函数
    • 实例编程

常用命令

进程间通讯(IPC机制):管道 信号量 共享内存 消息队列 套接字
查看进程的命令:
在一个终端执行

sleep(200)

在另一个终端查看该进程

ps -ef | grep "sleep"

显示和操作 共享内存 信号量 消息队列 的命令

ipcs

删除该信号量

ipcrm -s 2

删除共享内存

ipcrm -m 22201

管道

有名管道
管道类型的文件大小都为0
有名管道:任意进程
无名管道:父子进程

有名管道读写编程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
//write.c
int main(){
	int fdw = open( "fifo" ,0_WRONLY);
	if ( fdw == -1 )
	{
		printf( "open err\n ");
		exit(1);
	}
	while( 1 ){
		char buff[128] ={0};printf( "input: \n");
		fgets(buff,128,stdin);
		if ( strncmp(buff , "end" ,3) == 0 ){	break;		}
		write(fdw , buff,strlen( buff));
	}
	close( fdw);
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
//read.c
int main(){
	int fdr = open( "fifo" ,O_RDONLY);
	if ( fdr == -1 )
	{
		printf( "open err\n ");
		exit(1);
	}
	while( 1 ){
		char buff[128] ={0};
		int n=read(fdr ,buff,127);//如果管道没有数据,就会阻塞
		if(n==0)
		{
			printf( "it close pipe write terminal\n")
			break;
		}
		printf( " buff=(%d)=%s\n ",n,buff);
	}
	close(fdr);
}

在这里插入图片描述
注意:
如果直接关闭读端(ctrl+c),只剩写端,写端write写数据触发异常,写入数据就会异常终止 会受到该信号SIGPIPE
如果关闭写端(end 后者ctrl+c),读端会自动检测,读端read返回值为0,也会关闭

有名管道示意图

在这里插入图片描述

内核中管道的实现:
在这里插入图片描述
头指针负责写入,尾指针负责读,一个追上另一个,要么管道为满,要么为空,
管道为空时,读操作阻塞
管道为满,写操作阻塞

无名管道

没有名字,只能在父子进程之间使用,创建无无名管道

int pipe(int pipefd[2]);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
int main(){
	int fd[2];//操作管道描述符,fd[0],r,fd[1],w
	if(pipe(fd)==-1){//
		printf("pipe err\n");
		exit(0);
	}
	write(fd[1],"hello",5);
	sleep(5);
	char buff[128]={0};
	read(fd[0],buff,128);
	printf("buff=%s\n",buff);
	close(fd[0]);
	close(fd[1]);
	exit(0);
}
	

父子进程一个读一个写,让子进程读,父进程写

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
int main(){
	int fd[2];//操作管道描述符,fd[0],r,fd[1],w
	if(pipe(fd)==-1){
		printf("pipe err\n");
		exit(0);
	}
	pid_t pid = fork();
	if ( pid == -1 ){
		exit(0);
	}
	if ( pid == 0 ){
		close(fd[1]);
		char buff[128] = {0};
		read(fd[0],buff,127);
		printf("child buff=%s\[n" ,buff);close(fd[0]);
	}
	else{
		close(fd[0]);
		write(fd[1], "hello" ,5);
		close(fd[1]);
	}
	exit(0);
}	

无名管道是一个半双工
半双工 对讲机,能互相发送,但不能同时发
全双工:打电话,信息能同时相互传递
单共,收音机只能接收信号,不能发送信号,单向
写入管道的数据在内存,有名无名都是

有名和无名管道的区别:有名管道可以在任意进程间通讯,而无名只能在父子进程间通讯

信号量

信号量的概念

信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目,获取资源时,需要对信号量的值进行原子减一,该操作被称为 P 操作。当信号量值为 0 时,代表没有资源可用,P 操作会阻塞。释放资源时,需要对信号量的值进行原子加一,该操作被称为 V
操作。信号量主要用来同步进程。信号量的值如果只取 0,1,将其称为二值信号量。如果信号量的值大于 1,则称之为计数信号量。
临界资源:同一时刻,只允许被一个进程或线程访问的资源
临界区:访问临界资源的代码段

信号量接口函数

int semget(key_t key, int nsems, int semflg);

semget()创建或者获取已存在的信号量
semget()成功返回信号量的 ID, 失败返回-1
key:两个进程使用相同的 key 值,就可以使用同一个信号量
nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号
量的个数
semflg 可选: IPC_CREAT IPC_EXCL

int semop(int semid, struct sembuf *sops, unsigned nsops);

semop()成功返回 0,失败返回-1
这里要定义一个结构体

struct sembuf
{
	unsigned short sem_num; //指定信号量集中的信号量下标 为0 1 ...
	short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作 
	short sem_flg; //SEM_UNDO 
 };
int semctl(int semid, int semnum, int cmd, ...);

semctl()控制信号量,初始化和销毁需要使用
semctl()成功返回 0,失败返回-1
semnum 信号量集中对应信号量的下标
cmd 选项: SETVAL IPC_RMID
信号量id是一个信号量集的id,里面可能 有多个信号量,用下标区分
semun是一个联合体

union semun
 {
	int val;
	...
}

进程 a 和进程 b 模拟访问打印机 用信号量互斥

画图分析

进程 a 和进程 b 模拟访问打印机,进程 a 输出第一个字符‘a’表示开始使用打印
机,输出第二个字符‘a’表示结束使用,b 进程操作与 a 进程相同。(由于打印机同一时刻
只能被一个进程使用,所以输出结果不应该出现 abab),如图所示
在这里插入图片描述

代码实现

sem.h

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

union semun
{
    int val;
};
void sem_init();
void sem_p();
void sem_v();
void sem_destory();

sem.c

#include "sem.h"

static int semid = -1;
void sem_init()
{
    semid = semget((key_t)1234, 1, IPC_CREAT | IPC_EXCL | 0600);
    if (semid == -1)
    {
        semid = semget((key_t)1234, 1, 0600);
        if (semid == -1)
        {
            printf("semget err\n");
        }
    }
    else
    {
        union semun a;
        a.val = 1;
        if (semctl(semid, 0, SETVAL, a) == -1)
        {
            printf("set val err\n");
        }
    }
}

IPC_CREAT | IPC_EXCL 创建一个新的信号量,如果已经有的话,就会失败,直接获取就行。
semctl(semid, 0, SETVAL, a) 将信号量集中第一个信号量的初始值设置为a.val

void sem_p()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;
    buf.sem_flg = SEM_UNDO;
    if (semop(semid, &buf, 1) == -1)
    {
        printf("p err\n");
    }
}
void sem_v()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;
    buf.sem_flg = SEM_UNDO;
    if (semop(semid, &buf, 1) == -1)
    {
        printf("v err\n");
    }
}
void sem_destory()
{
    if (semctl(semid, 0, IPC_RMID) == -1)
    {
        printf("destroy err\n");
    }
}

semctl(semid, 0, IPC_RMID)销毁信号量

a.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"

int main()
{
    sem_init();
    for (int i = 0; i < 5; i++)
    {
        sem_p();
        printf("A");
        fflush(stdout);
        int n = rand() % 3;
        sleep(n);
        printf("A");
        fflush(stdout);
        sem_v();

        n = rand() % 3;
        sleep(n);
    }
}

b.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"
int main()
{
    sem_init();
    for (int i = 0; i < 5; i++)
    {
        sem_p();
        printf("B");
        fflush(stdout);
        int n = rand() % 3;
        sleep(n);
        printf("B");
        fflush(stdout);
        sem_v();

        n = rand() % 3;
        sleep(n);
    }
    sleep(10);//
    sem_destory();
}

sleep(10);只有一个信号量,所以只能销毁一次,那就睡眠10s,b后结束,在b里销毁

测试结果

在这里插入图片描述

显示和操作 共享内存 信号量 消息队列 的命令

ipcs

在这里插入图片描述
在这里插入图片描述
信号量用完一定要销毁,如果么有销毁,下次用如果用同一个信号量的话(id值相同),原本我全新创建,我要赋初值为10,但是已经存在了,获取值可能不是我所期望的,因此用完一定移除,万一在程序中出现问题,异常结束,可以用下面的命令
删除该信号量

ipcrm -s 2

删除共享内存

ipcrm -m 22201

共享内存

共享内存定义

共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是由malloc分配的一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。
在这里插入图片描述

共享内存函数接口

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

shmget()用于创建或者获取共享内存
shmget()成功返回共享内存的 ID, 失败返回-1
key: 不同的进程使用相同的 key 值可以获取到同一个共享内存
size: 创建共享内存时,指定要申请的共享内存空间大小
shmflg: IPC_CREAT IPC_

只显示共享内存

ipcs -m

映射

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

shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上
shmat()成功返回返回共享内存的首地址,失败返回 NULL
shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间
shmflg: 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写

int shmdt(const void *shmaddr);

shmdt()断开当前进程的 shmaddr 指向的共享内存映射
shmdt()成功返回 0, 失败返回-1

实例编程

图示理解

ps初始值为1 ps2初始值为0 ,ps1通过,将数据写到共享内存中,再执行vs2,通知test可以进行数据的打印,ps2可以通过,进行打印,打印结束后vs1,通知main可以进行输入,main执行一次后,只有test执行完毕后,才能再次执行
在这里插入图片描述

编码实现

//main.c

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

int main()
{
    int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0600);
    if (shmid == -1)
    {
        printf("shmget err\n");
        exit(1);
    }

    char *s = (char *)shmat(shmid, NULL, 0);
    if (s == (char *)-1)
    {
        printf("shmat err\n");
        exit(1);
    }

    sem_init();
    while (1)
    {
        printf("input:\n");
        char buff[128] = {0};

        fgets(buff, 128, stdin);

        sem_p(SEM1);
        strcpy(s, buff);
        sem_v(SEM2);

        if (strncmp(buff, "end", 3) == 0)
        {
            break;
        }
    }

    shmdt(s);
}

test.c

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

int main()
{
    int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0600);
    if (shmid == -1)
    {
        printf("shmget err\n");
        exit(1);
    }

    char *s = (char *)shmat(shmid, NULL, 0);
    if (s == (char *)-1)
    {
        printf("shmat err\n");
        exit(1);
    }

    sem_init();
    while (1)
    {
        sem_p(SEM2);
        if (strncmp(s, "end", 3) == 0)
        {
            break;
        }
        printf("s=%s\n", s);
        sem_v(SEM1);
    }

    shmdt(s);
    sem_destroy();
    shmctl(shmid, IPC_RMID, NULL);
}

sem.c

#include "sem.h"

static int semid = -1;
void sem_init()
{
    semid = semget((key_t)1234, SEM_NUM, IPC_CREAT | IPC_EXCL | 0600);
    if (semid == -1)
    {
        semid = semget((key_t)1234, SEM_NUM, 0600);
        if (semid == -1)
        {
            printf("semget err\n");
            return;
        }
    }
    else
    {
        int arr[SEM_NUM] = {1, 0};
        for (int i = 0; i < SEM_NUM; i++)
        {
            union semun a;
            a.val = arr[i];
            if (semctl(semid, i, SETVAL, a) == -1)
            {
                printf("semctl setval err\n");
            }
        }
    }
}
void sem_p(enum SEM_INDEX index)
{
    struct sembuf buf;
    buf.sem_num = index;
    buf.sem_op = -1; // p
    buf.sem_flg = SEM_UNDO;
    if (semop(semid, &buf, 1) == -1)
    {
        printf("semop p err\n");
    }
}
void sem_v(enum SEM_INDEX index)
{
    struct sembuf buf;
    buf.sem_num = index;
    buf.sem_op = 1; // v
    buf.sem_flg = SEM_UNDO;
    if (semop(semid, &buf, 1) == -1)
    {
        printf("semop v err\n");
    }
}
void sem_destroy()
{
    if (semctl(semid, 0, IPC_RMID) == -1)
    {
        printf("del err\n");
    }
}

sem.h

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/sem.h>
#define SEM_NUM 2
enum SEM_INDEX
{
    SEM1,
    SEM2
};
union semun
{
    int val;
};

void sem_init();
void sem_p(enum SEM_INDEX index);
void sem_v(enum SEM_INDEX index);
void sem_destroy();

测试结构

在这里插入图片描述
在这里插入图片描述

消息队列

图示理解

在这里插入图片描述

接口函数

struct mess
{
    long type;//1起步
    char buff[32]; // user data
};

msgget()创建或者获取一个消息队列
msgget()成功返回消息队列 ID,失败返回-1
msqflg: IPC_CREAT

int msgget(key_t key, int msqflg);
int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg);

msgsnd()发送一条消息
msgid 消息队列的id
(void*)&dt 将该消息结构体对象的地址传入队列
msqsz 消息结构体中有效数据的长度
msqflg 添加消息的时候,如果消息被放满,是阻塞,还是返回失败 默认是0 阻塞
一般设置为 0 可以设置 IPC_NOWAIT

ssize_t msgrcv(int msqid, void *msgp, size_t msqsz, long msqtyp, int msqflg);

msgrcv()接收一条消息
msgid 消息队列的id
(void*)&dt 将消息队列里面的数据读到dt结构体里
32消息结构体中buff[]的大小
msqtyp 消息类型 0 就是不区分类型
msqflg标志位 一般设置为 0 可以设置 IPC_NOWAIT

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgctl()控制消息队列
msgctl()成功返回 0,失败返回-1
cmd: IPC_RMID

实例编程

进程 a 发送一条消息,进程 b 读取消息
msga.c

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

struct mess
{
    long type;
    char buff[32]; // user data
};
int main()
{
    int msgid = msgget((key_t)1234, IPC_CREAT | 0600);
    if (msgid == -1)
    {
        exit(1);
    }
    struct mess dt;
    dt.type = 1;
    strcpy(dt.buff, "hello");
    msgsnd(msgid, (void *)&dt, 32, 0);
    return 0;
}

msgb.c

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

struct mess
{
    long type;
    char buff[32]; // user data
};

int main()
{
    int msgid = msgget((key_t)1234, IPC_CREAT | 0600);
    if (msgid == -1)
    {
        exit(1);
    }
    struct mess dt;
    msgrcv(msgid, (void *)&dt, 32, 1, 0);
    printf("read msg:%s\n", dt.buff);
    return 0;
}

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

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

相关文章

docker镜像push到仓库

镜像可以很方便直接 push 到 docker 的公共仓库或阿里云仓库 一、Dockerpush指定仓库是什么&#xff1f; Dockerpush是Docker的一个命令&#xff0c;用于将本地的Docker镜像推送到Docker官方公共仓库或用户私人仓库。而指定仓库则是将这个Docker镜像推送到指定的仓库中。 通过D…

【独立后台】快递小程序便宜寄快递系统小程序 对接易达

快递代发项目简介&#xff1a; 顾名思义就是帮发快递。原本产业链是客户-快递之间的联系&#xff0c;现在变成了客户-我们-快递&#xff0c;简单来说就是我们把客户聚集到一起团购到了更优惠的价格。很简单就是赚一个差价&#xff0c; 单子多就能和各个快递合作的平台&#x…

C++ 类型兼容规则

类型兼容规则是指在需要基类对象的任何地方&#xff0c;都可以使用公有派生类的对象来替代。 通过公有继承&#xff0c;派生类得到了基类中除构造函数和析构函数之外的所有成员。这样&#xff0c;公有派生类实际就具备了基类的所有功能&#xff0c;凡是基类能解决的问题&#x…

QA | 关于手持式频谱仪,您想了解的那些技术问题(二)

Q1&#xff1a;手持式频谱仪的灵敏度多高&#xff1f;底噪多少&#xff1f; 0.01-3GHz手持频谱仪的底噪/灵敏度为-128dBm RBW10kHz&#xff08;即归一化到Hz为-168dBm/Hz&#xff09;&#xff1b;2-8GHz手持频谱仪的底噪/灵敏度为-119dBm RBW30kHz&#xff08;即归一化到Hz为…

Javascript 数据结构[入门]

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…

JVM之类加载与字节码(一)

1.类文件结构 一个简单的HelloWorld.Java package cn.itcast.jvm.t5; // HelloWorld 示例 public class HelloWorld { public static void main(String[] args) { System.out.println("hello world"); } }编译为 HelloWorld.class 后的样子如下所示&#xff1a; […

最小二乘问题和非线性优化

最小二乘问题和非线性优化 0.引言1.最小二乘问题2.迭代下降法3.最速下降法4.牛顿法5.阻尼法6.高斯牛顿(GN)法7.莱文贝格马夸特(LM)法8.鲁棒核函数 0.引言 转载自此处&#xff0c;修正了一点小错误。 1.最小二乘问题 在求解 SLAM 中的最优状态估计问题时&#xff0c;我们一般…

ModaHub魔搭社区:大模型落地需要“记忆力”,这家公司想为向量数据库Milvus Cloud正名

现实生活中若两人进行对话,大致需要三步流程:一方首先抛出话题作引子;另一方会先调动记忆判断自己是否了解这个话题,然后再分析给出应该做出何种回答。如此循环往复直到互动结束,而此次对话又会作为一种新的“记忆”被双方吸收。 为让计算机完成这样的互动过程,并持续…

【云原生】Docker-Compose全方面学习

目录 1.compose简介 Compose V2 2.compose安装与下载 二进制包 PIP 安装 bash 补全命令 卸载 3.docker compose管理命令 命令对象与格式 命令选项 命令使用说明 1.compose简介 Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose&#xff0c;您可…

设计模式之策略模式(Strategy)

一、概述 定义一系列的算法&#xff0c;把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的类而变化。 二、适用性 1.许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。 2.需要使用一个算法的不同变体。…

Crowd-Robot Interaction 论文阅读

论文信息 题目&#xff1a;Crowd-Robot Interaction:Crowd-aware Robot Navigation with Attention-based Deep Reinforcement Learning 作者&#xff1a;Changan Chen, Y uejiang Liu 代码地址&#xff1a;https://github.com/vita-epfl/CrowdNav 来源&#xff1a;arXiv 时间…

面试测试开发被问到数据库索引不知道怎么办?

提出的问题 什么情况下创建索引&#xff0c;什么时候不需要索引&#xff1f; 索引的种类有哪些&#xff1f; 什么是索引 索引就是帮助数据库管理系统高效获取数据的数据结构&#xff0c;就好比一本书的目录&#xff0c;它可以帮我们快速进行特定值的定位与查找&#xff0c;…

软件架构师高级——3、数据库系统

• 数据库概述&#xff08;★★★&#xff09; 集中式数据库系统 •数据管理是集中的 •数据库系统的素有功能 &#xff08;从形式的用户接口到DBMS核心&#xff09; 者口集中在DBMS所在的计算机。 B/S结构 •客户端负责数据表示服务 •服务器主要负责数据库服务 •数据 和后端…

IC人才“疯狂”抢购:月薪开到7.5万的背后是什么?

随着人工智能和电动汽车等技术的快速发展&#xff0c;集成电路&#xff08;IC&#xff09;人才成为汽车行业的抢手货。近年来&#xff0c;车企对于IC人才的需求越来越大&#xff0c;导致月薪飙升到了7.5万的惊人高薪水。这个话题引起了广泛关注&#xff0c;下面我们将从供需关系…

卤味行业市场分析,绝味、周黑鸭、嘴尚绝谁能脱颖而出

随着人们生活水平的提高&#xff0c;卤味市场不断发展壮大&#xff0c;成为我国食品行业中一个重要的组成部分。根据国家统计局数据&#xff0c;截至2020年底&#xff0c;我国卤味店数量已经达到了8.4万家&#xff0c;总产值超过1600亿元。 卤味行业的特点 产品口味丰富&#…

布基纳法索ECTN(BESC)申请流程

根据BURKINA FASO布基纳法索签发于 11/07/2006法令编号 00557的规定: 自2006年11月07 日起所有出口至布基纳法索&#xff08;Burkina Faso&#xff09;的货物&#xff0c;必须申请ECTN/BESC。ECTN是ELECTRONIC CARGO TRACKING NOTE的英文缩写&#xff0c;BESC是BORDEREAU DE SU…

《大型网站技术架构设计》第二篇 架构-性能

不同视角下的网站性能 1、用户 从用户角度&#xff0c;网站性能就是用户在浏览器上直观感受到的网站响应速度快还是慢。用户感受到的时间。 2、开发人员 开发人员关注的主要是应用程序本身及其相关子系统的性能&#xff0c;包括响应延迟、系统吞吐量、并发处理能力、系统稳定…

Redis实战案例25-附近商铺功能

1. GEO数据结构 Redis中Geohash功能应用 添加地理坐标 求两点之间距离 搜索天安门附近10km的火车站&#xff0c;按升序 2. 导入店铺数据到GEO Redis中存储店铺的信息&#xff0c;将店铺的id和经纬度坐标存到GEO数据类型中去&#xff0c;其中member存id&#xff0c;经纬度对应…

关于自动化测试用例失败重试的一些思考

自动化测试用例失败重跑有助于提高自动化用例的稳定性&#xff0c;那我们来看一下&#xff0c;python和java生态里都有哪些具体做法&#xff1f; 怎么做 如果是在python生态里&#xff0c;用pytest做测试驱动&#xff0c;那么可以通过pytest的插件pytest-rerunfailures来实现…

第十三次CCF计算机软件能力认证

第一题&#xff1a;跳一跳 近来&#xff0c;跳一跳这款小游戏风靡全国&#xff0c;受到不少玩家的喜爱。 简化后的跳一跳规则如下&#xff1a;玩家每次从当前方块跳到下一个方块&#xff0c;如果没有跳到下一个方块上则游戏结束。 如果跳到了方块上&#xff0c;但没有跳到方块的…