嵌入式Linux学习笔记(5)-进程间常见通讯方式(c语言实现)
一、概述
进程间通信(IPC,InterProcess Communication)是指在多个进程之间进行数据传输和共享的机制。在操作系统中,进程是运行中的程序的实例,每个进程都有自己的内存空间和资源。
进程间通信可以用于在不同的进程之间传递数据、共享资源、进行协同工作等。常见的进程间通信方式有以下几种:
-
管道(Pipe):管道是一种半双工的通信方式,它是通过创建一个管道文件用于两个进程之间的通信。一个进程可以将数据写入管道,而另一个进程可以从管道中读取数据。
-
有名管道(Named Pipe):有名管道也是一种管道,不同之处在于它是一个带有名称的管道,可以用于多个进程之间的通信。
-
共享内存(Shared Memory):共享内存是一种进程间通信的高效方式,它允许多个进程访问同一块内存区域,从而实现数据共享。
-
消息队列(Message Queue):消息队列是一种按照先进先出(FIFO)原则进行数据传输的机制。进程可以将消息放入队列,而其他进程可以从队列中读取消息。
-
信号量(Semaphore):信号量是一个特殊的变量,用于控制多个进程之间的临界区访问。通过信号量可以实现进程的互斥和同步操作。
-
套接字(Socket):套接字是一种网络编程中常用的进程间通信方式,它可以在不同的主机之间进行数据传输和通信。
二、管道
1、用法
管道(pipe)是一种在进程间通信中非常常用的机制,它可以将一个进程的输出(stdout)直接作为另一个进程的输入(stdin)。
pipe()函数:创建管道使用,它的原型为:
int pipe(int pipefd[2]);
该函数需要传入一个整型的数组,数组的长度必须是2。该函数会创建一个管道,并将管道的读写句柄分别存储在pipefd[0]和pipefd[1]中。pipefd[0]用于读取管道中的数据,pipefd[1]用于写入管道。
常用函数有:
read()函数:从管道中读取数据。其原型为:
ssize_t read(int fd, void *buf, size_t count);
其中,fd是管道的读取句柄(即pipefd[0]),buf是存储读取数据的缓冲区,count是读取的最大字节数。该函数返回实际读取的字节数。如果从终端读取数据,fd 的值为 STDIN_FILENO。
write()函数:向管道中写入数据。其原型为:
ssize_t write(int fd, const void *buf, size_t count);
其中,fd是管道的写入句柄(即pipefd[1]),buf是待写入的数据的缓冲区,count是待写入的字节数。该函数返回实际写入的字节数。如果往终端写入数据,fd 的值为 STDOUT_FILENO。
close()函数:关闭管道的读取或写入端。其原型为:
int close(int fd);
其中,fd是待关闭的管道的句柄。
2、用例
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>int main(int argc, char const *argv[])
{pid_t pid;int pipefd[2];if (argc != 2){fprintf(stderr,"%s: 参数不够!\n",argv[0]);exit(EXIT_FAILURE);}//创建管道if (pipe(pipefd) == -1){perror("创建管道数据");exit(EXIT_FAILURE);}//创建子进程pid = fork();if (pid < 0){fprintf(stderr,"%s: 邀请新学员失败!\n",argv[0]);exit(EXIT_FAILURE);}else if (pid == 0){//读取管道数据并打印close(pipefd[1]);char str[100]={0};sprintf(str,"%d 读出数据",getpid());write(STDOUT_FILENO,&str,sizeof(str));char buf;while (read(pipefd[0],&buf,1)>0){write(STDOUT_FILENO,&buf,1);}write(STDOUT_FILENO,"\n",1);close(pipefd[0]);_exit(EXIT_SUCCESS);//直接退出不做任何操作}else{//写入管道数据,提供给子进程close(pipefd[0]);printf("%d 写入数据\n",getpid());write(pipefd[1],argv[1],strlen(argv[1]));close(pipefd[1]);waitpid(pid,NULL,0);}return 0;
}
这个用例创建了一个子进程。随后父进程写入数据,子进程读取数据。
三、有名管道
1、用法
有名管道(Named Pipe)是一种在操作系统中用于进程间通信的通道。它可以在不相关的进程之间传递数据。以下是对有名管道及常用函数的详细介绍。
有名管道是一种特殊的文件,它以文件名的形式存在于文件系统中。与普通文件不同的是,有名管道允许不相关的进程之间进行通信。有名管道是以FIFO(先进先出)的方式传递数据,即先进入管道的数据首先被读取出来。有名管道可以用于在父进程和子进程之间传递数据,也可以用于不同进程之间的通信。
mkfifo():创建一个新的有名管道。它的原型为:
int mkfifo(const char *pathname, mode_t mode)
其中,pathname是管道的路径名,mode定义了管道的权限。该函数成功时返回0,失败时返回-1。
open():打开有名管道以进行读取或写入操作。它的原型为:
int open(const char *pathname, int flags)
其中,pathname是管道的路径名,flags定义了打开的方式和权限。该函数成功时返回文件描述符,失败时返回-1。
其余函数如:read,write,close与管道用法一致。
有名管道提供了一种简单的进程间通信方式,但也有一些限制。首先,它只适用于在同一计算机上的进程间通信。其次,有名管道是阻塞式的,即读取操作会一直等待直到有数据可读,写入操作会一直等待直到有空间可写。因此,在使用有名管道时要注意避免进程间出现死锁的情况。
2、用例
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>int main(int argc, char const *argv[])
{int fd;//有名管道创建完可重复使用,但推荐每次用完删除,下次重新创建char *pipe_path = "/tmp/myfifo";if(mkfifo(pipe_path,0644) != 0){perror("mkfifo");if (errno != 17)//不是文件已存在{exit(EXIT_FAILURE);}}fd = open(pipe_path,O_WRONLY);if(fd == -1){perror("open");exit(EXIT_FAILURE);}char buf[100];ssize_t read_num;//从终端写入数据到管道中while ((read_num = read(STDIN_FILENO,buf,100)) > 0){write(fd,buf,read_num);}if (read_num < 0){perror("read");close(fd);exit(EXIT_FAILURE);}printf("发送数据到管道完成,进程终止\n");close(fd);//清除管道//清除对应特殊文件if (unlink(pipe_path) == -1){perror("unlink");}return 0;
}
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>int main(int argc, char const *argv[])
{int fd;char *pipe_path = "/tmp/myfifo";// if(mkfifo(pipe_path,0644) != 0){// perror("mkfifo");// exit(EXIT_FAILURE);// }fd = open(pipe_path,O_RDONLY);if(fd == -1){perror("open");exit(EXIT_FAILURE);}char buf[100];ssize_t read_num;//读取管道信息while ((read_num = read(fd,buf,100)) > 0){write(STDOUT_FILENO,buf,read_num);}if (read_num < 0){perror("read");close(fd);exit(EXIT_FAILURE);}printf("接收管道数据完成,进程终止\n");close(fd);return 0;
}
两个.c文件分别代表两个无关联的进程。一个从终端接收数据并将数据写入有名管道中,另一个从管道中接收数据并打印在终端。
四、共享内存
1、用法
在C语言中,进程间通信(IPC)是实现不同进程之间数据交换和共享的重要方式之一。共享内存是一种高效的IPC机制,可以实现不同进程之间的数据共享。
共享内存是一块由操作系统管理的内存区域,可以被多个进程同时访问。进程可以将共享内存映射到自己的地址空间中,从而可以直接读写该内存区域,实现数据共享。
shm_open():该函数用于创建或打开一个共享内存对象。它的原型如下:
int shm_open(const char *name, int oflag, mode_t mode);
参数:
name:共享内存对象的名称
oflag:打开标志,用于指定打开方式和操作权限
mode:权限位,用于指定该共享内存对象的操作权限
返回值:共享内存对象的文件描述符,若打开或创建失败则返回-1。
shm_unlink():该函数用于删除一个共享内存对象。它的原型如下:
int shm_unlink(const char *name);
参数:
name:共享内存对象的名称
返回值:成功返回0,失败返回-1。
mmap():该函数用于将共享内存映射到进程的地址空间中。它的原型如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数:
addr:指定映射的起始地址,通常设为NULL,让操作系统自动选择一个合适的地址。
length:映射的长度,单位为字节。
prot:映射内存区域的保护模式,如读、写、执行等。
flags:映射的标志,用于指定映射方式和映射类型。
fd:共享内存对象的文件描述符。
offset:共享内存对象中的偏移量。
返回值:映射成功时返回映射区域的起始地址,失败时返回MAP_FAILED。
munmap():函数用于释放由mmap函数映射的内存区域。它的原型如下:
int munmap(void *addr, size_t length);
参数:
addr:指向要释放的内存区域的起始地址。这个地址必须是mmap返回的地址,或者是页面大小的整数倍。
length:要释放的内存区域的大小。
返回值:成功时,返回0;失败时,返回-1,并设置errno。
2、用例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/types.h>int main(int argc, char const *argv[])
{char *share;// 1.创建共享内存对象char shm_name[100]={0};sprintf(shm_name,"/letter%d",getpid());int fd;fd = shm_open(shm_name,O_RDWR | O_CREAT,0644);if (fd < 0){perror("shm_open");exit(EXIT_FAILURE);}// 2.设置共享内存对象大小ftruncate(fd,1024);// 3.内存映射share = mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (share == MAP_FAILED){perror("mmap");exit(EXIT_FAILURE);}// 映射后关闭fd,不是释放close(fd);// 4.使用内存映射实现进程间的通讯pid_t pid = fork();if (pid < 0){perror("fork");exit(EXIT_FAILURE);}else if (pid == 0){strcpy(share,"hello,my name is liuyuhui\n");printf("子进程%d:发送完毕!\n",getpid());}else{waitpid(pid,NULL,0);printf("父进程 %d 接收到了子进程 %d 发送的消息: %s\n",getpid(),pid,share);// 5.释放映射区int re = munmap(share,1024);if (re == -1){perror("munmap");exit(EXIT_FAILURE);}}// 6.释放共享内存对象shm_unlink(shm_name);return 0;
}
共享内存大致就分了这六步。这个例程实现的是父进程向共享内存中写入数据,子进程从共享内存中读出数据。
五、消息队列
1、用法
在C语言中,进程间通讯是指两个或多个进程之间进行数据交换或共享资源的过程。消息队列是一种进程间通讯的方式,允许进程通过发送和接收消息来交换数据。
mq_open():用于创建或打开一个消息队列。函数原型如下:
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
参数:
name:消息队列的名称。
oflag:打开标志。可以是O_CREAT(如果消息队列不存在,则创建)和O_EXCL(与O_CREAT一起使用,确保创建新的消息队列)。
mode:权限和创建文件时的权限位掩码(只有在创建消息队列时才使用)。attr:指向消息队列属性的指针。
mq_unlink():用于删除一个已经命名的消息队列。函数原型如下:
int mq_unlink(const char *name);
参数:
name:消息队列的名称。
clock_gettime():用于获取当前时间。函数原型如下:
int clock_gettime(clockid_t clk_id, struct timespec *tp);
clk_id:时钟ID,可以是CLOCK_REALTIME(系统实时时间)或CLOCK_MONOTONIC(运行时间)。
tp:保存时间的结构体指针。
mq_timedsend():用于向消息队列发送消息,并可以设置发送超时时间。函数原型如下:
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
参数:
mqdes:消息队列描述符。
msg_ptr:指向发送消息的指针。
msg_len:发送消息的长度。
msg_prio:消息的优先级。
abs_timeout:绝对时间,若超过该时间则发送超时。
mq_timedreceive():用于从消息队列接收消息,并可以设置接收超时时间。函数原型如下:
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio, const struct timespec *abs_timeout);
参数:
mqdes:消息队列描述符。
msg_ptr:指向接收消息的指针。
msg_len:接收消息的最大长度。
msg_prio:接收到的消息的优先级。
abs_timeout:绝对时间,若超过该时间则接收超时
2、用例
①:父子进程间通讯
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{struct mq_attr attr;attr.mq_maxmsg=10;attr.mq_msgsize=100;attr.mq_flags=0;attr.mq_curmsgs=0;//创建消息队列char *mq_name = "/father_son"; mqd_t mqdes = mq_open(mq_name,O_RDWR|O_CREAT,0644,&attr);if (mqdes == -1){perror("mq_open");exit(EXIT_FAILURE);}//创建进程pid_t pid = fork();if (pid < 0){perror("fork");exit(EXIT_FAILURE);}else if (pid == 0){//子进程接收消息char read_buf[100];struct timespec time_info;//接受数据for (int i = 0; i < 10; i++){//清空并发送数据memset(read_buf,0,100);//设置等待时间clock_gettime(0,&time_info);time_info.tv_sec += 15;//接收消息if(mq_timedreceive(mqdes,read_buf,100,NULL,&time_info) == -1){perror("mq_timedreceive");}printf("子进程接收到消息:%s\n",read_buf);}}else{//父进程发送消息char send_buf[100];struct timespec time_info;for (int i = 0; i < 10; i++){//清空并发送数据memset(send_buf,0,100);sprintf(send_buf,"父进程发送的第 %d 条消息\n",(i+1));//获取当前时间clock_gettime(0,&time_info);time_info.tv_sec += 5;//发送消息if(mq_timedsend(mqdes,send_buf,strlen(send_buf),0,&time_info) == -1){perror("mq_timedsend");}printf("父进程休息1s\n");sleep(1);}} //释放两次消息队列的引用close(mqdes);//清除一次消息队列if (pid > 0){mq_unlink(mq_name);}return 0;
}
②:不同进程间通讯
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{struct mq_attr attr;attr.mq_maxmsg=10;attr.mq_msgsize=100;attr.mq_flags=0;attr.mq_curmsgs=0;//创建消息队列char *mq_name = "/p_c_mq"; mqd_t mqdes = mq_open(mq_name,O_RDWR|O_CREAT,0644,&attr);if (mqdes == -1){perror("mq_open");exit(EXIT_FAILURE);}char write_buf[100];struct timespec time_info;//不断接收控制台消息 发送到消息队列while (1){memset(write_buf,0,100);ssize_t read_count = read(STDIN_FILENO,write_buf,100);clock_gettime(0,&time_info);time_info.tv_sec += 5;if (read_count == -1){perror("read");continue;}else if (read_count == 0){//ctrl + d 退出时printf("EOF,exit...\n");char eof = EOF;if (mq_timedsend(mqdes,&eof,1,0,&time_info) == -1){perror("mq_timedsend");}break;}//正常发送if (mq_timedsend(mqdes,write_buf,strlen(write_buf),0,&time_info) == -1){perror("mq_timedsend");}printf("正常发送成功!\n");}//释放消息队列描述符close(mqdes);//清除消息队列 (消费者执行一次即可)// mq_unlink(mq_name);return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{struct mq_attr attr;attr.mq_maxmsg=10;attr.mq_msgsize=100;attr.mq_flags=0;attr.mq_curmsgs=0;//创建消息队列char *mq_name = "/p_c_mq"; mqd_t mqdes = mq_open(mq_name,O_RDWR|O_CREAT,0644,&attr);if (mqdes == -1){perror("mq_open");exit(EXIT_FAILURE);}char read_buf[100];struct timespec time_info;//不断接收控制台消息 发送到消息队列while (1){memset(read_buf,0,100);clock_gettime(0,&time_info);time_info.tv_sec += 15;//正常接收if (mq_timedreceive(mqdes,read_buf,100,NULL,&time_info) == -1){perror("mq_timedreceive");}//判断是否结束if (read_buf[0] == EOF){printf("收到结束信息,准备退出...\n");break;}printf("正常接收成功,信息为:%s\n",read_buf);}//释放消息队列描述符close(mqdes);//清除消息队列mq_unlink(mq_name);return 0;
}
需要注意的是因为公用同一片资源,所以在不同进程间通讯时接收方只需接收完消息后将消息队列清除掉即可,发送方不需要重复清除,也可避免接收方还没读取完消息队列的值就被发送方清除了消息队列。
相关文章:
嵌入式Linux学习笔记(5)-进程间常见通讯方式(c语言实现)
一、概述 进程间通信(IPC,InterProcess Communication)是指在多个进程之间进行数据传输和共享的机制。在操作系统中,进程是运行中的程序的实例,每个进程都有自己的内存空间和资源。 进程间通信可以用于在不同的进程之间…...
【移动端】菜单的自动展开与收回
前言 为了满足手机上菜单栏随用户移动,菜单的自动展示与隐藏,特此记录 基本原理 实现逻辑 window.addEventListener(‘scroll’, debouncedScrollHandler) – 监听文档视图滚动事件 document.querySelector(‘.header’) – 选择器匹配元素 创建show和h…...
Java获取Object中Value的方法
在Java中,获取对象(Object)中的值通常依赖于对象的类型以及我们希望访问的属性。由于Java是一种静态类型语言,直接从一个Object类型中访问属性是不可能的,因为Object是所有类的超类,但它本身不包含任何特定…...
集群聊天服务器项目【C++】(二)Json的简单使用
在上一章中,简单介绍了本项目的内容、技术栈、需求和目标等,详细介绍了环境配置,如果还没有配置成功,请参考我的上一篇博客环境配置 今天主要介绍Json库是什么以及简单的使用。 1.为什么要使用Json 我们在网络传输数据时&#…...
班迪录屏和这三款录屏工具,一键操作,太方便了!
嘿,小伙伴们!今天我要跟大家分享几款超棒的录屏工具,它们绝对是我们在工作和学习中不可或缺的好帮;这些工具功能强大且操作简单,下面就让我来详细介绍一下它们的使用体验和好用之处吧! 班迪录屏工具使用体…...
DAY60Bellman_ford 算法
队列优化算法 请找出从城市 1 到城市 n 的所有可能路径中,综合政府补贴后的最低运输成本。 如果能够从城市 1 到连通到城市 n, 请输出一个整数,表示运输成本。如果该整数是负数,则表示实现了盈利。如果从城市 1 没有路径可达城市…...
Dubbo SPI源码
文章目录 Dubbo SPI使用方式AOP功能源码剖析SPI注解1.获取加载器2.获取拓展实例对象3.创建拓展类的实例对象 Dubbo SPI Dubbo 的 SPI(Service Provider Interface)机制是一种强大的扩展机制,它允许开发者在运行时动态地替换或增加框架的功能。…...
《C++代码高度优化之双刃剑:避免过度优化引发的“暗雷”》
在 C编程的世界里,追求高效性能的代码是每个开发者的目标之一。高度优化的 C代码可以带来显著的性能提升,让程序在运行速度、内存占用等方面表现出色。然而,正如一把双刃剑,过度优化可能会引入难以察觉的错误,给程序带…...
javascript网页设计案例
设计一个具有良好用户体验的 JavaScript 网页涉及多个方面,如用户界面(UI)、用户体验(UX)、交互设计等。以下是一些示例案例,展示了如何使用 JavaScript 创建功能丰富且吸引人的网页设计。 1. 响应式导航菜…...
初阶数据结构【TOP】- 11.普通二叉树的介绍 - 1. (细致,保姆~~!)
文章目录 前言一、普通二叉树的链式结构二、 造树三、普通二叉树的遍历四、遍历完整代码五、总结 前言 本篇文章笔者将会对普通二叉树部分进行细致的讲解 , 本篇主要包括以下内容: 二叉树链式结构的介绍 ,二叉树的遍历. 笔者会一步一步分析带学者领略递归的美好~~ 一、普通二叉…...
【pyenv】pyenv安装版本超时的解决方案
目录 1、现象 2、分析现象 3、手动下载所需版本 4、存放到指定路径 5、重新安装 6、pip失败(做个记录,未找到原因) 7、方法二修改环境变量方法 7.1 设置环境变量 7.2 更新 7.3 安装即可 8、方法三修改XML文件 前言:研…...
【新片场-注册安全分析报告-无验证方式导致安全隐患】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 1. 暴力破解密码,造成用户信息泄露 2. 短信盗刷的安全问题,影响业务及导致用户投诉 3. 带来经济损失,尤其是后付费客户,风险巨大,造…...
新160个crackme - 057-bbbs-crackme04
运行分析 因软件版本老旧,需使用windows XP虚拟机运行有个SystemID,值为12345678需破解User ID和Password PE分析 yC壳,32位 OD手动脱壳 使用windows XP虚拟机,将程序拖入OD按一下F8,ESP变红,根据ESP定律设…...
车机中 Android Audio 音频常见问题分析方法实践小结
文章目录 前言1. 无声2. 断音3. 杂音4. 延迟播放5. 焦点问题6. 无声问题(连上 BT )其他完善中…… 前言 本文主要总结了一下车机开发中遇到的 Audio 有关的问题,同时参考网上的一案例,由于Audio 模块出现音频问题的场景很多,对每一个出现的问…...
湘大 OJ 代码仓库
有时候不需要上传一些题解,想要上传一些纯代码就行,傻傻把代码上传到文章里面,感觉效率不是很高,还是建立一个代码仓库比较方便 需要会使用魔法可能才能访问,github代码仓库地址...
Ruoyi Cloud K8s 部署
本文视频版本:https://www.bilibili.com/video/BV1xF4Se3Esv 参考 https://blog.csdn.net/Equent/article/details/137779505 https://blog.csdn.net/weixin_48711696/article/details/138117392 https://zhuanlan.zhihu.com/p/470647732 https://gitee.com/y_project/Ruo…...
OpenGL Texture C++ Camera Filter滤镜
基于OpenGL Texture纹理的强大功能,在片段着色器(Shader)中编写GLSL代码,对YUV的数据进行数据转换从而实现视频编辑软件中的相机滤镜功能。 接上一篇OpenGL Texture C 预览Camera视频的功能实现,本篇来实现Camera滤镜效…...
基于Sobel算法的边缘检测设计与实现
1、边缘检测 针对的时灰度图像,顾名思义,检测图像的边缘,是针对图像像素点的一种计算,目的时标识数字图像中灰度变化明显的点,图像的边缘检测,在保留了图像的重要结构信息的同时,剔除了可以认为…...
java:练习
编写一个 Java 程序,计算并输出从 1 到用户指定的数字 n 中,所有“幸运数字”。幸运数字的定义如下:条件 1:数字的所有位数(如个位、十位)加起来的和是 7 的倍数。条件 2:数字本身是一个质数&am…...
大数据中一些常用的集群启停命令
文章目录 一、HDFS二、MapReduce && YARN三、Hive 一、HDFS 格式化namenode # 确保以hadoop用户执行 su - hadoop # 格式化namenode hadoop namenode -format启动 # 一键启动hdfs集群 start-dfs.sh # 一键关闭hdfs集群 stop-dfs.sh# 如果遇到命令未找到的错误&#…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
