【Linux第八课-进程间通信】管道、共享内存、消息队列、信号量、信号、可重入函数、volatile

news2024/11/24 20:50:31

目录

  • 进程间通信
    • 为什么?
    • 是什么?
    • 怎么办?
      • 一般规律
      • 具体做法
    • 匿名管道
      • 原理
      • 代码
    • 命名管道
      • 原理
      • 代码
    • system V
      • 共享内存
      • 消息队列
      • 信号量
        • 信号量的接口
  • 信号
    • 概念
    • 为什么?
    • 怎么办?
      • 准备
      • 信号的产生
      • 信号的保存
        • 概念
        • 三张表匹配的操作和系统调用
      • 信号的处理
      • 内核态和用户态
    • 可重入函数
    • volatile
    • SIGCHLD

两个进程之间可以进行“数据”的之间传递吗?不能!进程具有独立性

进程间通信

为什么?

1、数据传输:一个进程需要将它的数据发送给另一个进程
2、资源共享:多个进程之间共享同样的资源。
3、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
4、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

是什么?

一个进程把自己的数据,能够交给另一个进程

怎么办?

一般规律

1、交换数据的空间(内存)
2、不能由通信双方任何一个提供!操作系统提供
进程间通信本质先让不同的进程,看到同一份资源(一般由OS提供)

具体做法

OS提供的“空间”有不同的样式,决定了不同的通信方式
1、管道(匿名、命名)
2、共享内存
3、消息队列
4、信号量

本地通信很少了,现在更多是基于网络的

匿名管道

原理

1、父进程以读方式、写方式打开一个文件
struct file是允许多个进程通过指针只想我的

在这里插入图片描述
参数int pipefd[2]:是个输出型参数,得到两个fd,pipefd[0]–r、pipefd[1]–w
返回值:0表示成功,-1表示错误】

代码

验证父子进程通信

(1)四种情况
情况一:管道内部没有数据 && 子进程不关闭自己的写端文件fd,读端(父)就要阻塞等待,直到pipe有数据
情况二:管道内部被写满 && 父进程(读端)不关闭自己的fd,写端(子)写满之后,就要阻塞等待
情况三:对于写端而言,不写了&&关闭了pipe,读端会将pipe中的数据读完,最后就会读到返回值为0,表示读结束,读到文件的结束
情况四:读端不读&&关闭,写端在写,OS会直接终止写端的进程(子进程),通过信号13)SIGPIPE信号杀掉进程
(2)五种特性
特征一:自带同步机制
特征二:血缘关系进行进程通信的,通常在父子
特征三:pipe是面向字节流的
特征四
特征五

应用场景代码 – 进程池

1、创建通信信道和子进程
2、控制子进程

a.选择一个进程通道
b.选择一个任务
c.发送任务

3、回收子进程

怎么让所有子进程退出:关闭进程池的写端,对应的子进程读到0就会相应退出了
怎么让所以已经退出的子进程(子进程僵尸)

问题:前面的进程的管道,除了父进程的写端指向,子进程的写端也会指向(子进程会拷贝父进程的文件描述符)

命名管道

原理

想要毫无关系的两个进程进行通信
让不同进程看到同一份资源

对于同一个文件,同一个进程以w、r两种方式打开,会产生两个struct_file但是指向的缓冲区和inode以及函数指针数组都是一样的
对于同一个文件,不同的进程打开,也会产生相应的struct_file,但是struct_file指向的缓冲区、函数指针数组、以及inode都是一样的

在这里插入图片描述
怎么保证两个进程打开同一个文件? 找到文件:文件的路径+文件名

需要特性的文件,不是上面的普通文件,不需要把数据刷新到磁盘
通过文件路径和文件名创建的特殊文件 – 命名管道

mkfifo

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

返回值
0:代表创建成功
其他:代表失败

代码

c_str() 是一个成员函数,通常用于std::string类。c_str()方法返回一个指向常量字符数组的指针,这个字符数组以 null 字符(‘\0’)结束,使得它可以兼容C语言的字符串处理函数。

#ifndef __COMM_HPP__
#define __COMM_HPP__

//....

#endif

#ifndef __COMM_HPP__:
#ifndef 是条件编译指令,表示“如果没有定义(if not defined)”。这里的 __COMM_HPP__ 是一个宏名,通常是根据文件名定义的。它检查这个宏是否已经被定义。
#define __COMM_HPP__:
如果 __COMM_HPP__ 这个宏没有被定义,接下来的代码就会被编译,并且会定义这个宏。这样,下一次再包含这个头文件时,#ifndef __COMM_HPP__ 的条件就会失败,从而跳过整个头文件的内容。
#endif
这条指令用于结束 #ifndef 的条件编译块。

服务端
1、创建管道文件
2、读管道
3、关闭管道

客户端
1、写管道

如果我们的写端没有打开,先读端打开,open的时候就会阻塞,直到把写端打开,读open才会返回 – 这是命名管道的特有特点

基于命名管道创建一个进程池

system V

上面是基于文件的
这个是内存中专门用于通信的
system V — 系统V (1)共享内存(2)消息队列(3)信号量

共享内存

1、创建共享内存
在这里插入图片描述
在这里插入图片描述

server获取key,创建共享内存
client获取key,获取共享内存
2、链接 – 将共享内存链接到进程的地址空间中
client、server都链接
在这里插入图片描述

进行通信

默认情况,shm读取方,根本就没管写入方
共享内存不提供两个进程间的任何协同的机制 — 缺点 — 数据不一致问题
共享内存是所有进程间通信速度最快的 ---- 这个空间没有系统调用,是用户空间 优点
由用户进行控制 – 信号量/用管道实现同步

server创建管道 – 协同机制

3、去除链接
在这里插入图片描述

4、删除共享内存
指令删除:ipcrm -m shmid
代码删除:shmctl
在这里插入图片描述

消息队列

一份公共的队列资源
创建共享队列
msgget

返回值msgid
在这里插入图片描述
删除共享队列
在这里插入图片描述

信号量

1、对于共享资源进行保护,是一个多执行流场景下,一个比较常见和重要的话题
2、互斥&&同步(访问资源在安全的前提下,具有一定的顺序性)
3、被保护起来的,任何时刻只允许一个执行流访问的公共资源 — 临界资源
4、访问临界资源的代码 — 临界区,所谓的保护公共资源(临界资源)–> 保护公共资源的本质:是程序员保护临界区
5、非临界区
6、原子性:操作对象的时候,只有两种状态,要么还没开始,要么已经结束

信号量(信号灯)
资源不一定被我持有,才是我的,只要我预定了,在未来的某个时间,就是我的

信号量:本质是一个计数器,描述临界资源数量的计数器
进程:(1)申请信号量 – P操作(2)访问资源(3)释放信号量 – V操作

多进程场景,int能不能实现信号量的效果?
不能,
(1)int无法在进程间共享 – 让不同的进程先看到同一份资源 — 计数器资源
(2)count++和count–不是原子的

二元信号量就是一把锁

信号量的接口

申请信号量
semget

你创建一个信号量,怎么让另一个进程看到,用同一个key即可
nsems:你想要创建几个信号量
semid返回值,信号量集标识符
档位数组就行
在这里插入图片描述

删除信号量
semctl

semnum:要对哪个信号量进行操作
cmd:删除信号量IPC_RMID

在这里插入图片描述

PV操作
semop

semid:哪个信号量集的标识符
sops
nsops:对哪个信号量进行操作
在这里插入图片描述
sem_num:对哪个信号量
sem_op:做什么操作
sem_flg:默认为0即可以

在这里插入图片描述

信号

信号vs信号量没有任何关系

概念

Linux系统提供的让用户(进程)给其他进程发送一部信息的一种方式

1、在没有发生的时候,我们已经知道发生的时候,怎么处理了
2、信号我们能够认识,之前有人给我的大脑中设置了识别特定信号的方式
3、信号到来的时候,我们正在处理更重要的事情,我们暂时不能处理到来的信号,我们必须暂时将到来的的信号进行临时保存
4、信号到来,可以不立即处理,可以在合适的时候处理
5、信号的产生是随时产生的,无法准确预料,所以信号是异步发送的(信号的产生,由别人(用户/进程)产生的,我收到之前,我一直在忙我的事情,并发在跑)

为什么?

停止,删除。系统要求进程有随时响应外部信号的能力,随后作出反应

怎么办?

准备

1、kill -l:数字名字都可以识别信号,名字其实就是宏

没有0、32、33
34-64:实时信号,来了必须立即处理
man 7 signal:查信号具体含义
在这里插入图片描述

2、信号的处理方式 ---- signal 可以更改我对信号处理的方式!
a、默认动作
b、自定义处理信号 — 捕捉
c、忽略了信号 ---- 是处理了信号吗?是

发送2号信号,默认动作,终止进程
在这里插入图片描述

signal
signum:对几号信号进行捕捉
handler:函数指针
返回值:老的处理方法
信号处理一次,往后都有效
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<unistd.h>
#include<signal.h>

void handler(int Sig)
{
    std::cout << "I capture a signal..." << std::endl;
}

int main()
{
    signal(2, handler);
    while(true)
    {
        std::cout << "I am activiting..., mypid:" << getpid() << std::endl;
        sleep(1);
    }
    return 0;
}

捕捉退出码echo $?

忽略信号:SIG_IGN
第二个参数设为上面那个
在这里插入图片描述

int main()
{
    signal(2, SIG_IGN);
    while(true)
    {
        std::cout << "I am activiting..., mypid:" << getpid() << std::endl;
        sleep(1);
    }
    return 0;
}

信号的产生

1、kill命令

kill -9 pid

2、键盘产生
ctrl + c:OS解释成为2号信号,向目标进程发生,进程收到,进程响应

代码
ctrl + \:3号信号
ctrl z:19号暂停信号

3、系统调用
kill:对任意进程发送任意信号
在这里插入图片描述
代码图

raise:对自己发生任意信号
在这里插入图片描述

abort:终止进程,对自己发生指定信号 — 6号信号SIGABERT
在这里插入图片描述
4、软件条件

例如管道的读端关闭,写端写时,写入条件不具备就会被发送13号信号,终止进程

闹钟alarm,在seconds秒之后发送SIGALRM

返回值:0/以前设置的闹钟还剩多少秒

在这里插入图片描述

int main()
{
    alarm(1);
    int cnt = 0;
    while(true)
    {
        std::cout << "cnt: " << cnt++ << std::endl;
    }
    return 0;
}

在这里插入图片描述

设置一次,闹钟只会响一次
可以在捕捉函数里重新设置一个闹钟
在这里插入图片描述

void handler(int Sig)
{
    std::cout << "I capture a alarm..." << std::endl;
    alarm(2);
}

int main()
{
    signal(SIGALRM, handler);
    alarm(5);
    int cnt = 0;
    while(true)
    {
        sleep(1);
        std::cout << "cnt: " << cnt++ << std::endl;
    }-
    return 0;
}

闹钟提前响了会返回前一个闹钟的剩余描述
在这里插入图片描述

void handler(int Sig)
{
    int n = alarm(2);
    std::cout << "Last alarm: " << n << std::endl;
    
}

int main()
{
    signal(SIGALRM, handler);
    alarm(50);
    int cnt = 0;
    while(true)
    {
        sleep(1);
        std::cout << "cnt: " << cnt++ << ", PID:" << getpid() << std::endl;
    }
    return 0;
}

alarm(0):取消闹钟
在这里插入图片描述

int main()
{
    //signal(SIGALRM, handler);
    alarm(50);
    int cnt = 5;
    while(cnt)
    {
        sleep(1);
        std::cout << "cnt: " << cnt-- << ", PID:" << getpid() << std::endl;
    }
    int n = alarm(0);
    std::cout << "alarm(0), n:" << n << std::endl;
    return 0;
}

设定闹钟,其实是OS内部设定的,可能存在很多的闹钟。管理闹钟?先描述,在组织

5、异常
a.代码除0 – 8 SIGFPE
b.野指针 – 11 SIGSEGV

核心转储

man 7 signal
进程终止:core/term
在这里插入图片描述

在这里插入图片描述

云服务器默认将core退出,进行了特定的设定,默认core是被关闭的
如何打开linux的core功能呢?
ulimit -a
在这里插入图片描述

ulimit -c 10240:10240设置文件大小随便设置
在这里插入图片描述

为什么有core?想通过core定位到进程为什么退出,以及执行到哪行代码退出的
是什么?将进程在内存中的核心数据(与调试有关)转储到磁盘中形成core/core.pid的文件,core dump(核心转储)
有什么用?协助我们进行调试,gdb利用core文件定位错误core-file core
在这里插入图片描述

信号的保存

概念

信号递达(Delivery):实际执行信号的处理动作称为信号递达:默认、忽略、自定义
信号未决(Pending):信号从产生到递达之间的状态,称为信号未决。
进程可以选择阻塞 (Block )某个信号。进程收到某些信号也可以不做处理,屏蔽(阻塞)信号
如果一个信号被阻塞(屏蔽),则该信号永远不会被递达处理,除非解除阻塞
阻塞vs忽略:忽略是一种信号递达的方式,阻塞仅仅是不让指定信号进程递达
在这里插入图片描述

在这里插入图片描述
block表:1 - 阻塞;0 - 不阻塞

三张表匹配的操作和系统调用

1、block和pending表的数据结构
sigset_t:信号集,用户级信号类型,结构体
block表:阻塞信号集,也称信号屏蔽字
2、对sigset_t数据类型的操作函数

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);

3、修改block位图
sigprocmask

how:怎么操作,三种类型
set:要对哪些位置进行操作,写到色图集合里面
oset:输出型参数,把老的信号屏蔽字输出出来
在这里插入图片描述
在这里插入图片描述
4、修改pending位图
sigpending
set:输出型参数
在这里插入图片描述

第一个场景
a.屏蔽2号信号
b.未来我们给进程发2号信号 – 2号信号不会被抵达 – 2号信号就会一直在pending位图中
c.获取打印pending位图

9号、19号信号不允许屏蔽,18号信号会被特殊处理

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cassert>
void Print(sigset_t &pending)
{
    std::cout << "pending bitmap:";
    for (int i = 32; i > 0; i--)
    {
        int n = sigismember(&pending, i);
        if (n == 0)
        {
            std::cout << 0;
        }
        else
        {
            std::cout << 1;
        }
    }
    std::cout << std::endl;
}

int main()
{
    sigset_t block;
    sigset_t oblock;
    sigemptyset(&block);
    sigemptyset(&oblock);
    sigaddset(&block, 2);
    // 1、屏蔽2号信号
    int n = sigprocmask(SIG_BLOCK, &block, &oblock);
    assert(n == 0);
    std::cout << "pid: " << getpid() <<std::endl;
    while (true)
    {
        // 2、获取pending位图
        sigset_t pending;
        sigemptyset(&pending);
        n = sigpending(&pending);
        assert(n==0);

        // 3、打印pending
        Print(pending);
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

第二个场景
2号信号被递达,从1变成0

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cassert>
void Print(sigset_t &pending)
{
    std::cout << "pending bitmap:";
    for (int i = 32; i > 0; i--)
    {
        int n = sigismember(&pending, i);
        if (n == 0)
        {
            std::cout << 0;
        }
        else
        {
            std::cout << 1;
        }
    }
    std::cout << std::endl;
}
void handler(int segno)
{
    std::cout << "2号信号递达处理中..." << std::endl;
}

int main()
{
    //对2号信号的递达操作进行自定义
    signal(2, handler);
    sigset_t block;
    sigset_t oblock;
    sigemptyset(&block);
    sigemptyset(&oblock);
    sigaddset(&block, 2);
    // 1、屏蔽2号信号
    int n = sigprocmask(SIG_BLOCK, &block, &oblock);
    assert(n == 0);
    std::cout << "pid: " << getpid() <<std::endl;
    int cnt = 0;
    while (true)
    {
        if(cnt == 20)
        {
            //取消2号信号的屏蔽
            n = sigprocmask(SIG_UNBLOCK, &block, &oblock);
            assert(n==0);
        }
        // 2、获取pending位图
        sigset_t pending;
        sigemptyset(&pending);
        n = sigpending(&pending);
        assert(n==0);

        // 3、打印pending
        Print(pending);
        sleep(1);
        cnt++;
    }
    return 0;
}

在这里插入图片描述

先清0,再递达;还是先递达,再清0?先清0,再递达
在2号信号抵达处理的过程中去打印block表,如果此时为0了 – 就是先清0,再递达;如果此时为1 – 那就是先递达,再清0

信号的处理

1、信号什么时候被处理
合适的时候,什么是合适的时候?进程从内核态,切换回用户态的时候,信号被检出并处理
用过系统调用 — 陷入内核 — OS执行系统调用 — 结果给用户

即便你的代码没有系统调用,但是进程切换本身也会内核态到用户态的切换(在CPU上运行用户态)

2、信号如何被处理
自己写的handler方法在用户态执行
在这里插入图片描述

3、捕捉信号还有其他方法吗?
sigaction:对一个指定信号进行捕捉

signum:

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

sa_mflags:默认0就行
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字
如果除了当前正在处理的信号,还想屏蔽其他信号,用sa_mask

内核态和用户态

1、我们使用系统调用或者访问系统数据,其实还是在我进程地址空间内进行跳转的
2、进程无论如何切换,总能找到OS。我们访问OS,本质就是通过我的进程的地址空间的[3,4]GB来访问
系统调用表、系统调用号

操作系统然后跑起来的?
信号技术本来就是通过软件的方式,来模拟的硬件中断

OS的周期时钟中断,不断发送给操作系统,操作系统处理时钟中断跑起来

可重入函数

函数被执行流(main执行流、信号捕捉执行流)重复进入了,产生问题的函数称不可重入函数,不会产生问题的函数称可重入函数
我们用到的大部分函数都是不可重用的

volatile

CPU里面的计算:逻辑运算、数据运算

#include<stdio.h>
#include<signal.h>

int flag = 0;
void handler(int signo)
{
    printf("charge flag: %d -> %d\n", flag, 1);
    flag = 1;
}

int main()
{
    signal(2, handler);
    while(!flag);

    printf("process quit success\n");
    return 0;
}
gcc test.c

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

gcc test.c -O1

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

当对一个变量的值进行逻辑判断的时候,如果整个main函数中未对该变量进行修改,编译器默认会对我们的代码进行自动优化,将改变量的值直接把存在寄存器中,寄存器屏蔽了内存
解决:volatile修饰一个变量时,不允许将变量直接存在寄存器里,必须从内存中读取
在这里插入图片描述
在这里插入图片描述

加个{}可能就没有这个现象了

SIGCHLD

子进程退出,不会默默退出,会给父进程发生信号的 – SGCHLD
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <signal.h>

void handler(int signo)
{
    std::cout << "child quit, father get a signo: " << signo << std::endl;
}

int main()
{
    signal(SIGCHLD, handler);
    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt--)
        {
            std::cout << "I am child process:" << getpid() << std::endl;

            sleep(1);
        }
        exit(0);
    }

    while(true)
    {
        
    }
    return 0;
}

以前只能:waitpid/wait阻塞等、waitpid/wait非阻塞轮询等待子进程
父进程把子进程回收的代码方法放到信号处理函数中

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void handler(int signo)
{
    std::cout << "child quit, father get a signo: " << signo << std::endl;
    int n = waitpid(-1, nullptr, 0);

}

int main()
{
    signal(SIGCHLD, handler);
    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt--)
        {
            std::cout << "I am child process:" << getpid() << std::endl;

            sleep(1);
        }
        exit(0);
    }

    while(true)
    {

    }
    return 0;
}

但是同时大量子进程退出时,pending位图只会记录一次 — 循环式的回收
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void handler(int signo)
{
    while (true)
    {
        std::cout << "child quit, father get a signo: " << signo << std::endl;
        int n = waitpid(-1, nullptr, 0);
        if (n < 0)
        {
            break;
        }
    }
    std::cout << "child quit done" << std::endl;
}

int main()
{
    signal(SIGCHLD, handler);
    for (int i = 0; i < 100; i++)
    {
        pid_t id = fork();
        if (id == 0)
        {
            // child
            int cnt = 5;
            while (cnt--)
            {
                std::cout << "I am child process:" << getpid() << std::endl;

                sleep(1);
            }
            exit(0);
        }
    }


    return 0;
}

但当五十个退、五十个不退。第五十一个还会调用waitpid,第五十一个不会退出,那就会一直阻塞等,就回不去mian函数了 — waitpid函数里面的0改成WNOHANG非阻塞等待

int n = waitpid(-1, nullptr, WNOHANG);

让子进程退出方法
手动忽略,子进程退出后,父进程不用等待它 — 只linux平台下这样
不关心子进程的退出信息

signal(SIGCHLD, SIG_IGN)

系统的IGN,OS做了特殊处理将子进程变成僵尸

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

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

相关文章

文件互传助手 v1.0

电脑和手机互传文件&#xff0c;经常需要找数据线&#xff0c;连着数据线还要下载安装手机助手&#xff0c;还要安装驱动识别手机&#xff0c;还要点手机那个连接模式&#xff0c;实在太麻烦了。 明明是一件简单的事情&#xff0c;往往需要很多层手续。有时会用到微信网页端作文…

Spring Boot 与 Vue 共筑航空机票预定卓越平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

面试总结!

OSI七层模型&#xff1a; 什么是OSI七层模型&#xff1f; 我们需要了解互联网的本质是一系列的网络协议&#xff0c;这个协议就叫做OSI协议&#xff08;开放系统互联(Open System Interconnection&#xff09;&#xff09;&#xff0c;它是由ISO&#xff08;国际标准化组织&…

MATLAB实现智能水滴算法(Intelligent Water Drops Algorithm, IWDA)

1.智能水滴算法介绍 智能水滴算法&#xff08;Intelligent Water Drops Algorithm&#xff0c;IWDA&#xff09;是一种基于水滴特性的智能优化算法&#xff0c;它借鉴了水滴在自然界中的运动和形态变化规律&#xff0c;通过模拟水滴的形成、发展和消亡过程&#xff0c;实现问题…

RabbitMQ延迟队列(重要)

RabbitMQ延迟队列 1、延迟队列1.1、延迟队列使用场景1.2、延迟队列实现原理 2、使用rabbitmq-delayed-message-exchange 延迟插件2.1、下载2.2、安装2.2.1、解压2.2.2、启用插件2.2.3、查询安装情况 2.4、示例2.4.1、RabbitConfig配置类&#xff08;关键代码&#xff09;2.4.2、…

Tencent Hunyuan3D

一、前言 腾讯于2024年11月5日正式开源了最新的MoE模型“混元Large”以及混元3D生成大模型“Hunyuan3D-1.0”&#xff0c;支持企业及开发者在精调、部署等不同场景下的使用需求。 GitHub - Tencent/Hunyuan3D-1 二、技术与原理 Hunyuan3D-1.0 是一款支持文本生成3D&#xff08;…

新品发布:广州大彩科技DB系列5.0寸带CAN/RS485外壳串口屏发布!

新品发布:广州大彩科技DB系列5.0寸带CAN/RS485外壳串口屏发布! 一、产品介绍 该产品是一款5寸的医疗级外壳系列组态串口屏&#xff0c;液晶屏采用800*480分辨率&#xff0c;拥有电阻触摸。硬件上针对工控行业&#xff0c;汽车行业串口使用RS485电平&#xff0c;并且另外加入了…

Js — 定时器

有两种&#xff1a;setInterval 和 setTimeout 间隔时间单位为毫秒 setInterval 每隔指定的毫秒数重复执行一个函数或代码 开启定时器&#xff1a;setInterval(函数&#xff0c;间隔时间) 作用&#xff1a;每隔一段时间调用这个函数 注意&#xff1a;它不是立即执行&#x…

WPF+MVVM案例实战与特效(二十六)- 3D粒子方块波浪墙效果实现

文章目录 1、案例效果2、案例实现1、文件创建2. 功能代码实现3、粒子功能应用1、前端布局与样式2、代码解释2、 后端功能代码1、案例效果 2、案例实现 1、文件创建 打开 Wpf_Examples 项目、Models 文件夹下创建 3D粒子模型类 ParticleCubeWaveModel.cs 文件。在Tools 文件夹…

Linux命令 - linux索引节点、硬链接、软链接的介绍与使用

文章目录 1 索引节点inode2 硬链接Hard Link3 软链接Soft Link 1 索引节点inode 在Linux系统中&#xff0c;保存在磁盘分区中的文件&#xff0c;不管是什么类型&#xff0c;系统都会给它分配一个编号&#xff0c;这个编号被称为索引节点编号&#xff08;Inode Index&#xff0…

基于Python通过DOI下载文献(至简仅需2行代码)

文章目录 一、安装库二、导入库三、准备doi3.1 excel法3.1.1 检索数据3.1.2 导出excel 3.2 txt法3.3 列表or字符串法3.3.1 字符串3.3.2 列表 四、下载4.1 脚本4.2 下载成功4.3 已存在4.4 至于失败的 五、结果5.1 目标文件夹5.2 失败记录 一、安装库 pip install OAFuncs 二、导…

SpringBoot技术在企业资产管理中的应用

4系统概要设计 4.1概述 系统设计原则 以技术先进、系统实用、结构合理、产品主流、低成本、低维护量作为基本建设原则&#xff0c;规划系统的整体构架. 先进性&#xff1a; 在产品设计上&#xff0c;整个系统软硬件设备的设计符合高新技术的潮流&#xff0c;媒体数字化、压缩、…

OpenHarmony4.1蓝牙芯片如何适配?触觉智能RK3568主板SBC3568演示

当打开蓝牙后没有反应时&#xff0c;需要排查蓝牙节点是否对应、固件是否加载成功&#xff0c;本文介绍开源鸿蒙OpenHarmony4.1系统下适配蓝牙的方法&#xff0c;触觉智能SBC3568主板演示 修改对应节点 开发板蓝牙硬件连接为UART1&#xff0c;修改对应的节点&#xff0c;路径为…

招标采购系统(源码+文档+部署+讲解)

本文将深入解析“招标采购系统&#xff08;供应商管理系统&#xff09;”的项目&#xff0c;探究其架构、功能以及技术栈&#xff0c;并分享获取完整源码的途径。 系统概述 包含基础数据管理、供应商和专家库管理&#xff0c;还涉及招标代理机构、政策法规和文件模板的管理。…

javascript里面的blob和worker

目录 Blob 1. Blob的基本概念 2. 创建Blob 3. Blob的属性和方法 示例&#xff1a; 3.1. Blob 的方法 4. 使用Blob 4.1 创建对象URL 4.2 使用FileReader读取Blob 4.3 上传Blob 5. Blob与其他对象的关系 6. 释放Blob对象 7. Blob的应用场景 8. 总结 Web Worker 1.…

Android JNI 技术入门指南

引言 在Android开发中&#xff0c;Java是一种主要的编程语言&#xff0c;然而&#xff0c;对于一些性能要求较高的场景&#xff08;如音视频处理、图像处理、计算密集型任务等&#xff09;&#xff0c;我们可能需要使用到C或C等语言来编写底层的高效代码。为了实现Java代码与C…

设计模式之抽象工厂模式(替换Redis双集群升级,代理类抽象场景)

前言&#xff1a; 看了很多书、学了很多知识&#xff0c;多线程能玩出花&#xff0c;可最后我还是写不好代码&#xff01; 这就有点像家里装修完了买物件&#xff0c;我几十万的实木沙发&#xff0c;怎么放这里就不好看。同样代码写的不好并不一定是基础技术不足&#xff0c;也…

LeetCode 热题 100之 堆

1.数组中第k个最大元素 和Acwing 786 第k个数一模一样 排序 思路分析1&#xff1a;此题要求时间复杂度未为O(n)。虽然库函数sort和快速排序都能过&#xff0c;但是时间复杂度不满足条件。下面优化快速排序&#xff0c;写一个快速选择算法。我们可以引入随机化来加速这个过程&…

java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl

用大模型做车牌号识别&#xff0c;最简单高效 在Java场景中&#xff0c;java识别车牌号的需求非常普遍。过去&#xff0c;我们主要依赖OCR等传统方法来实现java识别车牌号&#xff0c;但这些方法的效果往往不稳定。随着技术的发展&#xff0c;现在有了更先进的解决方案——大模…

java ssm 网上蛋糕店 在线蛋糕甜品管理 网上蛋糕管理 源码 jsp

一、项目简介 本项目是一套基于SSM的网上蛋糕店&#xff0c;主要针对计算机相关专业的和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本、软件工具等。 项目都经过严格调试&#xff0c;确保可以运行&#xff01; 二、技术实现 ​后端技术&#xff1a;S…