当前位置: 首页 > news >正文

嵌入式Linux学习笔记(5)-进程间常见通讯方式(c语言实现)

一、概述

        进程间通信(IPC,InterProcess Communication)是指在多个进程之间进行数据传输和共享的机制。在操作系统中,进程是运行中的程序的实例,每个进程都有自己的内存空间和资源。

        进程间通信可以用于在不同的进程之间传递数据、共享资源、进行协同工作等。常见的进程间通信方式有以下几种:

  1. 管道(Pipe):管道是一种半双工的通信方式,它是通过创建一个管道文件用于两个进程之间的通信。一个进程可以将数据写入管道,而另一个进程可以从管道中读取数据。

  2. 有名管道(Named Pipe):有名管道也是一种管道,不同之处在于它是一个带有名称的管道,可以用于多个进程之间的通信。

  3. 共享内存(Shared Memory):共享内存是一种进程间通信的高效方式,它允许多个进程访问同一块内存区域,从而实现数据共享。

  4. 消息队列(Message Queue):消息队列是一种按照先进先出(FIFO)原则进行数据传输的机制。进程可以将消息放入队列,而其他进程可以从队列中读取消息。

  5. 信号量(Semaphore):信号量是一个特殊的变量,用于控制多个进程之间的临界区访问。通过信号量可以实现进程的互斥和同步操作。

  6. 套接字(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语言实现)

一、概述 进程间通信&#xff08;IPC&#xff0c;InterProcess Communication&#xff09;是指在多个进程之间进行数据传输和共享的机制。在操作系统中&#xff0c;进程是运行中的程序的实例&#xff0c;每个进程都有自己的内存空间和资源。 进程间通信可以用于在不同的进程之间…...

【移动端】菜单的自动展开与收回

前言 为了满足手机上菜单栏随用户移动&#xff0c;菜单的自动展示与隐藏&#xff0c;特此记录 基本原理 实现逻辑 window.addEventListener(‘scroll’, debouncedScrollHandler) – 监听文档视图滚动事件 document.querySelector(‘.header’) – 选择器匹配元素 创建show和h…...

Java获取Object中Value的方法

在Java中&#xff0c;获取对象&#xff08;Object&#xff09;中的值通常依赖于对象的类型以及我们希望访问的属性。由于Java是一种静态类型语言&#xff0c;直接从一个Object类型中访问属性是不可能的&#xff0c;因为Object是所有类的超类&#xff0c;但它本身不包含任何特定…...

集群聊天服务器项目【C++】(二)Json的简单使用

在上一章中&#xff0c;简单介绍了本项目的内容、技术栈、需求和目标等&#xff0c;详细介绍了环境配置&#xff0c;如果还没有配置成功&#xff0c;请参考我的上一篇博客环境配置 今天主要介绍Json库是什么以及简单的使用。 1.为什么要使用Json 我们在网络传输数据时&#…...

班迪录屏和这三款录屏工具,一键操作,太方便了!

嘿&#xff0c;小伙伴们&#xff01;今天我要跟大家分享几款超棒的录屏工具&#xff0c;它们绝对是我们在工作和学习中不可或缺的好帮&#xff1b;这些工具功能强大且操作简单&#xff0c;下面就让我来详细介绍一下它们的使用体验和好用之处吧&#xff01; 班迪录屏工具使用体…...

DAY60Bellman_ford 算法

队列优化算法 请找出从城市 1 到城市 n 的所有可能路径中&#xff0c;综合政府补贴后的最低运输成本。 如果能够从城市 1 到连通到城市 n&#xff0c; 请输出一个整数&#xff0c;表示运输成本。如果该整数是负数&#xff0c;则表示实现了盈利。如果从城市 1 没有路径可达城市…...

Dubbo SPI源码

文章目录 Dubbo SPI使用方式AOP功能源码剖析SPI注解1.获取加载器2.获取拓展实例对象3.创建拓展类的实例对象 Dubbo SPI Dubbo 的 SPI&#xff08;Service Provider Interface&#xff09;机制是一种强大的扩展机制&#xff0c;它允许开发者在运行时动态地替换或增加框架的功能。…...

《C++代码高度优化之双刃剑:避免过度优化引发的“暗雷”》

在 C编程的世界里&#xff0c;追求高效性能的代码是每个开发者的目标之一。高度优化的 C代码可以带来显著的性能提升&#xff0c;让程序在运行速度、内存占用等方面表现出色。然而&#xff0c;正如一把双刃剑&#xff0c;过度优化可能会引入难以察觉的错误&#xff0c;给程序带…...

javascript网页设计案例

设计一个具有良好用户体验的 JavaScript 网页涉及多个方面&#xff0c;如用户界面&#xff08;UI&#xff09;、用户体验&#xff08;UX&#xff09;、交互设计等。以下是一些示例案例&#xff0c;展示了如何使用 JavaScript 创建功能丰富且吸引人的网页设计。 1. 响应式导航菜…...

初阶数据结构【TOP】- 11.普通二叉树的介绍 - 1. (细致,保姆~~!)

文章目录 前言一、普通二叉树的链式结构二、 造树三、普通二叉树的遍历四、遍历完整代码五、总结 前言 本篇文章笔者将会对普通二叉树部分进行细致的讲解 , 本篇主要包括以下内容: 二叉树链式结构的介绍 ,二叉树的遍历. 笔者会一步一步分析带学者领略递归的美好~~ 一、普通二叉…...

【pyenv】pyenv安装版本超时的解决方案

目录 1、现象 2、分析现象 3、手动下载所需版本 4、存放到指定路径 5、重新安装 6、pip失败&#xff08;做个记录&#xff0c;未找到原因&#xff09; 7、方法二修改环境变量方法 7.1 设置环境变量 7.2 更新 7.3 安装即可 8、方法三修改XML文件 前言&#xff1a;研…...

【新片场-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…...

新160个crackme - 057-bbbs-crackme04

运行分析 因软件版本老旧&#xff0c;需使用windows XP虚拟机运行有个SystemID&#xff0c;值为12345678需破解User ID和Password PE分析 yC壳&#xff0c;32位 OD手动脱壳 使用windows XP虚拟机&#xff0c;将程序拖入OD按一下F8&#xff0c;ESP变红&#xff0c;根据ESP定律设…...

车机中 Android Audio 音频常见问题分析方法实践小结

文章目录 前言1. 无声2. 断音3. 杂音4. 延迟播放5. 焦点问题6. 无声问题(连上 BT )其他完善中…… 前言 本文主要总结了一下车机开发中遇到的 Audio 有关的问题&#xff0c;同时参考网上的一案例&#xff0c;由于Audio 模块出现音频问题的场景很多&#xff0c;对每一个出现的问…...

湘大 OJ 代码仓库

有时候不需要上传一些题解&#xff0c;想要上传一些纯代码就行&#xff0c;傻傻把代码上传到文章里面&#xff0c;感觉效率不是很高&#xff0c;还是建立一个代码仓库比较方便 需要会使用魔法可能才能访问&#xff0c;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纹理的强大功能&#xff0c;在片段着色器&#xff08;Shader&#xff09;中编写GLSL代码&#xff0c;对YUV的数据进行数据转换从而实现视频编辑软件中的相机滤镜功能。 接上一篇OpenGL Texture C 预览Camera视频的功能实现&#xff0c;本篇来实现Camera滤镜效…...

基于Sobel算法的边缘检测设计与实现

1、边缘检测 针对的时灰度图像&#xff0c;顾名思义&#xff0c;检测图像的边缘&#xff0c;是针对图像像素点的一种计算&#xff0c;目的时标识数字图像中灰度变化明显的点&#xff0c;图像的边缘检测&#xff0c;在保留了图像的重要结构信息的同时&#xff0c;剔除了可以认为…...

java:练习

编写一个 Java 程序&#xff0c;计算并输出从 1 到用户指定的数字 n 中&#xff0c;所有“幸运数字”。幸运数字的定义如下&#xff1a;条件 1&#xff1a;数字的所有位数&#xff08;如个位、十位&#xff09;加起来的和是 7 的倍数。条件 2&#xff1a;数字本身是一个质数&am…...

大数据中一些常用的集群启停命令

文章目录 一、HDFS二、MapReduce && YARN三、Hive 一、HDFS 格式化namenode # 确保以hadoop用户执行 su - hadoop # 格式化namenode hadoop namenode -format启动 # 一键启动hdfs集群 start-dfs.sh # 一键关闭hdfs集群 stop-dfs.sh# 如果遇到命令未找到的错误&#…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...