C语言-Linux进程间通信方式

news2024/9/29 21:29:53

1、进程间通信方式概述

进程间通信方式有:
    管道(Pipo)和有名管道(FIFO):用于具有亲缘关系进行间通信,有名管道,允许无亲缘关系进程间的通信
    信号(Signal):比较复杂的通信方式,用于通知接收进程有某种事件发生
    消息队列:消息的连接表,包括Posix消息队列和System V消息队列
    信号量(Signal)/信号灯:主要被用作进程间或同一进程不同线程之间的同步手段
    共享内存:最有用的进程间通信方式,需要某种同步机制,互斥锁和信号量都可以
    套接字:用于不同机器之间的进程间通信

进程间通信的目的
    数据传输、共享数据、通知事件、资源共享、进程控制

2、管道通信

特点:
    半双工,数据只能一个方向流动,进行双方通信的时候要建立两个管道,用于父子间或兄弟进程间通信
    一个进程向管道中写的数据内容被管道另一端的进程读出
    写入的数据每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据
    一般的I/O函数都可以用于管道,如close、read、write等

读数据步骤:
    若写端不存在,则认为以及读到了数据的末尾,读函数返回读出的字节数为0
    若管道的写端存在,请求的字节数目大于PIPE_BUE(定义在include/linux/limits中),返回现有字节数,不大于的话,返回现有字节数或请求字节数
    在管道写端关闭后,写入的数据将一直存在,直到被读出为止

写端对读端的依赖性:
    在向进程中写入数据时,至少应该存在某一个进程,其中管道读端没有被关闭。否则就会出现管道断裂,进程收到SIGPIPE信号,默认动作为进程终止

2.1 创建无名管道

函数详解 

表头文件
    #include <unistd.h>
    
定义函数
    int pipe(int filedes[2]);
    
函数说明
    用于建立管道
    将文件描述符由参数filedes数组返回
    filedes[0] 为管道的读取端
    filedes[1] 为管道的写入端
    
返回值
    成功返回零,否则返回-1

综合案例 

#include <stdio.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
	int filedes[2];
	char buffer[80];
	pipe(filedes);
	if(fork() > 0)
	{
		char s[] = "hello!\n";
		write(filedes[1],s,sizeof(s));
	}
	else
	{
		read(filedes[0],buffer,80);
		printf("father %s",buffer);
	}

	return 0;
}

运行结果

linux@ubuntu:~/test$ gcc pipe_dome.c -o pipe_dome
linux@ubuntu:~/test$ ./pipe_dome 
father hello!

2.2 创建有名管道

函数详解  

表头文件
    #include <sys/types.h>
    #include <sys/stat.h>
    
定义函数
    int mkfifo(const char* pathname,mode_t mode);
    
函数说明
    pathname: 建立特殊的FIFO文件,该文件必须不存在
    mode: 描述pahtname文件的权限
    当使用open()打开文件时,O_NONBLOCK旗标会有下列影响:
        当使用O_NONBLOCK旗标时,打开文件来读取操作会立刻返回;若没有其他进程打开文件来读取,则写入操作会返回ENXIO错误
        没有使用O_NONBLOCK旗标时,打开FIFO文件读取操作会等到其他进程打开FIFO文件进行写入才能正常返回。同样,打开FIFO文件进行写入操作会等其他进程打开FIFO文件进程读取后才能正常返回

 综合案例 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/home/linux/test/2"

int main(int argc,char *argv[])
{
	char buffer[80];
	int fd;
	unlink(FIFO);                 //unlink删除指定的文件,若文件存在则删除它,确保后边mkfifo建立的特殊文件不存在
	mkfifo(FIFO,0666);
	if(fork() > 0)
	{
		char s[] = "hello!\n";
		fd = open(FIFO,O_WRONLY);
		write(fd,s,sizeof(s));
		close(fd);
	}
	else
	{
		fd = open(FIFO,O_RDONLY);
		read(fd,buffer,80);
		printf("%s",buffer);
		close(fd);
	}

	return 0;
}

 运行结果

linux@ubuntu:~/test$ gcc mkfifo.c -o mkfifo
linux@ubuntu:~/test$ ./mkfifo
hello!

3、信号

信号的本质:
    在软件层次上对中断机制的一种模拟。异步的

信号来源:
    硬件来源:按下了键盘或者其他硬件故障
    软件来源:发送信号的系统函数:kill、raise、alarm、setitimer、非法运算操作

分类:
    性能:
        可靠:克服了信号可能丢失问题,这些信号支持排队
        不可靠:从UNIX继承过来的信号,进程每次处理信号后,就将对信号的相应设置为默认动作,有时候会错误处理,或信号丢失
    时间:
        实时:支持排队,都是可靠信号
        非实时:不支持排队,是不可靠信号

信号的处理方式:
    忽略信号:即对信号不做任何处理,SIGKILL和SIGSTOP不能忽略
    捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数
    执行默认操作:进程对实时信号的默认操作反应是进程终止

信号发送的主要函数有:
    kill、raise、sigqueue、alarm、setitimer、abort

3.1 信号发送函数kill和alarm

函数详解   

表头文件
    #include <sys/types.h>
    #include <signal.h>
    
定义函数
    int kill(pid_t pid,int sig);
    
函数说明
    用于发送信号给指定进程
    pid: 指定的进程
        pid>0:将信号传递给指定的pid进程
        pid=0:将信号传给目前进程相同的进程组的所有进程
        pid=-1:将信号广播传送给系统内所有的进程
        pid<0:将信号传给进程组识别码为pid绝对值的所有进程
    sig:指定要传送的信号
    
返回值
    执行成功将返回0,如果有错误则返回-1

 综合案例  

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

int main(int argc,char *argv[])
{
	pid_t pid;
	int status;
	pid = fork();

	if(pid == 0)
	{
		printf("I an child process!\n");
		sleep(10);
	}
	else
	{
		printf("send signal to child process (%d)\n",pid);
		sleep(1);
		kill(pid,SIGABRT);                    //向子进程中发送信号6
		wait(&status);                        //等待子进程中断或结束,将结束信号返回
		if(WIFSIGNALED(status))               //判断子进程是否因信号而结束
			printf("child process receive signal %d\n",WTERMSIG(status));   //获取子进程因信号而中止的信号代码
	}
	return 0;
}

  运行结果

linux@ubuntu:~/test$ gcc kill_dome.c -o kill_dome
linux@ubuntu:~/test$ ./kill_dome 
send signal to child process (25564)
I an child process!
child process receive signal 6

 函数详解   

表头文件
    #include <unistd.h>
    
定义函数
    unsigned int alarm(unsigned int seconds);
    
函数说明
    用来设置信号传送闹钟
    设置信号SIGALRM,在经过seconds指定的秒数后传送给目前的进程
    若参数为0,则之前设置的闹钟会被取消,剩下的时间返回
    
返回值
    返回之前闹钟剩余的秒数,如果之前未设置闹钟则返回0

 综合案例   

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

void handler()
{
	printf("hello\n");
}

int main(int argc,char *argv[])
{
	int i;
	signal(SIGALRM,handler);
	alarm(5);
	for(i = 1;i < 7;i++)
	{
		printf("sleep %d ..\n",i);
		sleep(1);
	}
	return 0;
}

 运行结果

linux@ubuntu:~/test$ gcc ararm_dome.c -o alarm_dome
linux@ubuntu:~/test$ ./alarm_dome
sleep 1 ..
sleep 2 ..
sleep 3 ..
sleep 4 ..
sleep 5 ..
hello
sleep 6 ..

3.2 自定义信号处理方式

3.2.1 signal函数

  函数详解   

表头文件
    #include <signal.h>
​
定义函数
    void (*signal(int signum,void(*handler)(int)))(int);
​
函数说明
    用于传送信号给指定的进程
    signum: 指定信号编号来设置该信号的处理函数
    void(*handler)(int): 信号处理函数
        如果参数handler不是函数指针,则必是下列两个常数之一
            SIG_IGN:忽略参数signum指定信号
            SIG_DFL:将参数signum指定的信号重设为核心预设的信号处理方式
​
返回值
    返回先去的信号处理函数指针,若错误则返回-1
    
注意:
    信号跳转到自定的handler处理函数后,系统会自动将此函数换回原来系统预设的处理方式,若要改变此操作,采用sigaction

  综合案例   

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

void my_func(int sign_no)
{
	if(sign_no == SIGINT)
		printf("I have get SIGINT\n");
	else if(sign_no == SIGQUIT)
		printf("I have get SIGQUIT\n");
}

int main(int argc,char *argv[])
{
	printf("Waiting for signal SIGINT or SIGQUIT\n");

	/*注册信号处理函数*/
	signal(SIGINT,my_func);
	signal(SIGQUIT,my_func);

	pause();   //暂停主进程,直到收到信号
	exit(0);
	return 0;
}

 运行结果

linux@ubuntu:~/test$ ./signal_dome 
Waiting for signal SIGINT or SIGQUIT
I have get SIGQUIT

//新开窗口
linux@ubuntu:~$ ps -aux|grep signal_dome
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
linux    25920  0.1  0.5  15872  5560 pts/2    S+   19:02   0:01 vi signal_dome.c
linux    25992  0.0  0.0   1988   280 pts/3    T    19:15   0:00 ./signal_dome
linux    26000  0.0  0.0   1988   284 pts/3    S+   19:16   0:00 ./signal_dome
linux    26064  0.0  0.0   6044   836 pts/5    T    19:16   0:00 grep --color=auto -l signal_dome
linux    26071  0.0  0.0   6044   836 pts/5    S+   19:17   0:00 grep --color=auto signal_dome
linux@ubuntu:~$ kill -SIGQUIT 26000                            //向暂停的进程发送信号

3.2.2 sigaction函数

表头文件
    #include <signal>
    
定义函数
    int sigaction(int signum,const struct sigaction *act,struct sigaction * oldact);
    
函数说明
    用于查询或设置信号处理方式
    signum:指定信号编号来处理函数
    sigaction定义如下:
        struct sigaction
        {
            void (*sa_handler)(int);  //新处理函数,同signal函数的参数handler
            sigset_t sa_mask;         //处理该信号时暂时将sa_mask信号搁置
            int sa_flags;             //用来处理信号的其他相关操作
            //A_NOCLDSTOP:如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程
            //SA_ONESHOT/SA_RESETHAND:在调用新的信号处理函数前,将此信号处理方式改为系统预设的方式
            //SA_RESTART:被信号中断的系统调用会自行重启
            //SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来
            void (*sa_restorer)(void);   //已过时,POSIX不支持它
        }
    若参数oldact不是NULL指针,则原来的信号处理方式会由结构sigaction返回
    
返回值
    成功返回0,错误返回-1

3.3 信号集操作

信号集被定义为一种数据类型:
typedef struct
{
    unsigned long sig[_NSIG_WORDS];
}sigset_t

3.3.1 sigemptyset函数

表头文件
    #include <signal.h>
    
定义函数
    int sigemptyset(sigset_t *set);
    
函数说明
    用于初始化信号集
    将参数set初始化,并清空
    
返回值
    执行成功返回0,错误返回-1

3.3.2 sigfillset函数

表头文件
    #include <signal.h>
​
定义函数
    int sigfillset(sigset_t *set);
​
函数说明
    用于将所有信号加入信号集
    将参数set初始化,然后把所有信号加入此信号集里
    
返回值
    成功返回0.错误返回-1

3.3.3 sigaddset()函数

表头文件
    #include <signal.h>
​
定义函数
    int sigaddset(sigset_t *set,int signum);
​
函数说明
    增加一个信号到信号集
    用来将参数signum代表的信号加入至set中
​
返回值
    成功返回0.错误返回-1

3.3.4 sigdelset函数

表头文件
    #include <signal.h>
​
定义函数
    int sigdelset(sigset_t *set,int signum);
​
函数说明
    用于从信号集中删除一个信号
    用来将参数signum代表的信号从参数set中删除
​
返回值
    成功返回0.错误返回-1

3.3.5 sigismember函数

表头文件
    #include <signal.h>
​
定义函数
    int sigismember(const sigset_t *set,int signum);
​
函数说明
    测试某个信号是否已加入信号集内
    用来测试参数signum代表的信号是否加入参数set中
​
返回值
    成功返回0.错误返回-1

4、消息队列

概念
    消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式和特定的优先级。对消息队列有写权限的进程可以按照一定的规则添加新消息,对于消息队列有读权限的进程则可以从消息队列中读走消息,消息队列是随内核持续的

类型
    POSIX消息队列和系统V消息队列,考虑程序的移植性,尽量用POSIX消息队列

基础理论
    系统V消息队列是随内核持续的,只有在内核重启或者显示删除一个消息队列时,该消息队列才会真正被删除
    系统中记录消息队列的数据结构(struct ipc_ids msg_ids)位于内核中  消息队列可以在msg_ids中找到访问入口
    每个消息都有一个队列头,用struct msg_queue来描述

对消息队列的操作
    打开或创建消息队列:要获得消息队列描述词,需提供消息队列的键值
    读写消息队列:每个消息都有类似如下的数据结构
        struct msgbuf
        {
            long mtype;        //消息类型
            char mtext[1];     //消息内容,长度不一定为1
        }
        故对于发送消息来说,需要预置一个msgbuf缓冲区并写入消息内容,读同样
    获取或设置消息队列属性

4.1 ftok函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    
定义函数
    key_t ftok(char* pathname,char proj);
​
函数说明
    用于将文件名转化成键值
    pathname:相应的一个文件,必须存在,且在程序的访问范围内
    char:任意一个字符
​
返回值
    返回与文件对应的键值

4.2 msgget函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int msgget(key_t key,int msgflg);
​
函数说明
    用于创建一个消息队列
    key:一个键值
    msgflg:标志位
        IPC_CREAT、IPC_EXCL、IPC_NOWAIT
    若没有消息队列与键值key对应,且msgflg包含IPC_CREAT标志位或参数key位IPC_PRIVATE则将创建一个新的消息队列
​
返回值
    成功返回队列描述词,否则返回-1

4.3 msgrcv函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int msgrcv(int msqid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg);
​
函数说明
    用于读出消息队列的数据
    msqid:消息队列描述词
    *msgp:将消息存在msgp所指向的msgbuf结构中
    msgsz:指定msgbuf的mtext的长度
    msgtyp:请求读取的消息类型
    msgflg:控制消息队列中没有相应类型的消息可以接收时将发生的事情
        IPC_NOWAIT:没有返回条件的消息,调用立即返回ENOMSG
​
返回值
    成功则返回读出消息的实际字节数,否则返回-1

4.4 msgsnd函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int msgsnd(int msqid,struct msgbuf *msgp,int msgsz,int msgflg);
​
函数说明
    用于向消息队列中写入数据
    msqid:消息队列描述词
    *msgp:将存在msgp所指向的msgbuf结构中的消息进行发送
    msgsz:消息的大小
    msgflg:有意义的msgflg标志为IPC_NOWAIT,表明消息队列没有足够的空间容纳要发送的消息时,msgsnd是否等待
​
返回值
    成功返回0,否则返回-1

4.5 msgctl函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int msgctl(int msqid,int cmd,struct msqid_ds *buf);
​
函数说明
    用于控制消息队列
    msqid:消息队列描述词
    cmd:
        IPC_STAT:获取消息队列信息,返回的信息存储在buf指向的msqid结构中
        IPC_SET:设置消息队列的属性,设置的属性存储在buf指向的msqid结构中
        IPC_RMID:删除msqid标识的消息队列
​
返回值
    成功返回0,否则返回-1

5、信号灯(了解)

作用:
    提供对进程间共享资源的访问控制机制,相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源

类型:
    二值信号灯:最简单的信号灯形式,信号灯的值只能取0或1,类似于互斥锁
    计算信号灯:信号灯的值可以取任意非负值

内核实现原理:
    系统V信号灯也是随内核持续的。在内核中由数据结构struct ipc_ids sem_ids记录,所有信号灯都可以在结构sem_ids中找到访问入口

信号灯的使用
    打开或创建信号灯:与消息队列的创建及打开相同
    信号灯值操作:linux可以加减信号灯的值,相当于对共享资源的释放和占有
    获得或设置信号灯属性:系统中的每一个信号灯集都对应一个struct sem_array结构,该结构记录了信号灯集的各种信息,存在于系统空间。为了设置、获得该信号灯集的各种信息和属性,在用户空间有一个重要的联合结构与之对应,即union semun

5.1 semget函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int semget(key_t key,int nsems,int semflg);
​
函数说明
    配置信号灯
    key:是一个键值,由ftok获得,标识唯一一个信号灯集,用法与msgget中的key相同
    nsems:指定打开或者新创建的信号灯集中包含的信号灯的数目
    semflg:标志位,与msgget相同
    
返回值
    成功返回信号灯集描述词,否则返回-1

5.2 semop函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int semop(int semid,struct sembuf *sops,unsigned nsops);
​
函数说明
    用于信号灯处理
    semid:信号集ID
    sops:指向数组的每一个sembuf结构都描述一个特定信号灯上的操作
    nsops:sops数组的大小
    sembuf的结构为:
        struct sembuf{
            unsigned short sem_num;     //对应集中的信号灯,0对应第一个信号灯
            short          sem_op;      //大于0,等于0,小于0确定了对sem_num指定的信号灯进行的3种操作
            short          sem_flg;     //IPC_NOWAIT和SEM_UNDO两个标志
        }
​
返回值
    成功返回0,否则返回-1

5.3 semctl函数

表头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
​
定义函数
    int semctl(int semid,int semnum,int cmd,union semun arg);
​
函数说明
    用于控制信号灯
    semid:指定信号灯集
    cmd:具体的操作类型
        IPC_STAT:获取信号灯信息,由arg.buf返回
        IPC_SET:设置信号灯信息,设置信息保存在arg.buf中
        CETALL:返回所有信号灯信息,结果保存在arg.array中
        CETNCNT:返回等待semnum所代表信号灯的值增加的进程数
        GETPID:返回最后一个对semnum所代表信号灯执行semop操作的进程ID
        GETVAL:返回semnum所代表信号灯的值
        GETZCNT:返回等待semnum所代表信号灯的值变成0的进程数
        SETALL:设置semnum所代表信号灯的值为arg.val

6、共享内存方式

概念
    共享内存是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A,B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间,A和B都能看到对方内存中数据的更新,由于共用一块内存,必然需要某种同步机制,互斥锁和信号量都可以

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

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

相关文章

在github上,如何只下载选中的文件?

GitHub官方不直接支持下载子目录&#xff0c;但可以使用特定的第三方工具或脚本来实现这一需求。 总而言之一句话&#xff1a;需要下载插件&#xff01;&#xff01;&#xff01;具体实操步骤如下&#xff1a; 1.打开谷歌浏览器右上角的管理扩展程序&#xff1a; 2.搜索GitZi…

NLP任务之预测最后一个词

目录 1.加载预训练模型 2 从本地加载数据集 3.数据集处理 4.下游任务模型 5.测试代码 6.训练代码 7.保存训练好的模型 8. 加载 保存的模型 1.加载预训练模型 #加载预训练模型 from transformers import AutoTokenizer#预训练模型&#xff1a;distilgpt2 #use_fast…

《无机杀手》制作团队选择Blender的原因分析

《无机杀手》&#xff08;Murder Drones&#xff09;是一部备受欢迎的动画短片&#xff0c;其制作团队选择使用Blender软件进行制作&#xff0c;这一选择背后有着多方面的原因。【成都渲染101--blender渲染农场邀请码6666提供文案参考】 开源且免费 Blender是一个开源且免费的…

什么是数字化转型?数字化转型对企业有哪些优势?

一、什么是数字化转型&#xff1f; 定义&#xff1a; 数字化转型是指企业或组织将传统业务转化为数字化业务&#xff0c;利用人工智能、大数据、云计算、区块链、5G等数字技术提升业务效率和质量的过程。通俗来说&#xff0c;就是将数字技术应用到企业的各个方面&#xff0c;…

贝锐蒲公英网盘首发,秒建私有云,高速远程访问

虽然公共网盘带来了不少便利&#xff0c;但是大家对隐私泄露和重要数据泄密的担忧也随之增加。如果想要确保数据安全&#xff0c;自建私有云似乎是一条出路&#xff0c;然而面对搭建私有云的复杂步骤&#xff0c;许多人感到力不从心&#xff0c;NAS设备的成本也往往让人望而却步…

【MySQL】数据库中的内置函数

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 目录 函数 日期函数 字符串函数 数学函数 ​编辑 其它函数 MySQL数据库提供了大量的内置函数&#xff0c;这些函数可以帮助你执行各种操作&#xff0c;如字符串处理、数值计算、日期和时间处理等&#xff01; 函数…

云计算Openstack Keystone

OpenStack Keystone是OpenStack平台中的一个核心组件&#xff0c;主要负责身份认证和授权管理服务。以下是关于OpenStack Keystone的详细介绍&#xff1a; 一、作用 身份认证&#xff1a;Keystone为OpenStack平台提供统一的身份认证服务&#xff0c;管理所有用户&#xff08;…

ElasticSearch系列:【Win10环境(版本8.11.1) 】elasticsearch+kibana纪实

一、环境 安装环境&#xff1a;win10 JDK&#xff1a;1.8 elasticsearch&#xff1a;8.11.1 kibana&#xff1a;8.11.1 下载地址1&#xff08;elasticsearchkibana&#xff09;&#xff1a;Past Releases of Elastic Stack Software | Elastic i下载地址2&#xff08;k分…

RS HMP4040 直流电源

R&S HMP404 直流电源 苏州新利通仪器仪表 产品综述 单台仪器中最多四个通道 R&SHMP4000 直流电源具有三个或四个输出通道&#xff0c;每个通道的输出电流高达 10 A&#xff0c;主要设计用于工业应用&#xff0c;例如&#xff1a; -生产测试 -维护 -工程实验室 这些…

关于git分支冲突问题

什么是冲突 在Git中&#xff0c;冲突是指两个或多个开发者对同一文件统一部份进行了不同的修改&#xff0c;并且在合并这些修改时&#xff0c;Git无法自动确定应该采用哪种修改而产生的情况。 分支冲突 如何出现并解决 在一个版本时&#xff0c;有一个master分支&#xff0c…

JAVA甜蜜升级情侣专属扭蛋机游戏系统小程序源码

甜蜜升级&#xff01;情侣专属扭蛋机游戏系统&#xff0c;让爱更有趣&#x1f496; &#x1f389; 开篇&#xff1a;爱的游戏新玩法 在爱情的旅途中&#xff0c;我们总在寻找那些能让彼此心跳加速、笑容满面的瞬间。现在&#xff0c;“甜蜜升级情侣专属扭蛋机游戏系统”为你和…

用友畅捷通-TPlus FileUploadHandler.ashx 任意文件上传

0x01 产品描述&#xff1a; ‌用友畅捷通-TPlus‌是由用友集团成员企业畅捷通公司开发的一款企业级财务管理工具&#xff0c;旨在帮助企业实现财务管理的现代化和智能化。作为畅捷通旗下的核心产品&#xff0c;TPlus集成了财务核算、资金管理、预算控制等多项核心功能&#xff…

spring boot 项目中redis的使用,key=value值 如何用命令行来查询并设置值。

1、有一个老项目&#xff0c;用到了网易云信&#xff0c;然后这里面有一个AppKey&#xff0c;然后调用的时候要在header中加入这些标识&#xff0c;进行与服务器进行交互。 2、开发将其存在了redis中&#xff0c;一开始的时候&#xff0c;我们测试用的老的key&#xff0c;然后提…

结合创新!小波变换+注意力机制,实现100%分类准确率

小波变换是一种先进的信号分析技术&#xff0c;它擅长捕捉信号的局部特征&#xff0c;但有时可能会忽略数据中的关键信息。为了克服这一局限&#xff0c;我们引入了注意力机制&#xff0c;这一机制能够强化模型对数据重要部分的关注。通过将小波变换与注意力机制相结合&#xf…

SD2.0 Specification之CRC(Cyclic Redundancy Code)

文章目录 本文章主要讲解关于SD2.0中的CRC应用&#xff0c;其它基础概念和其它内容请参考以下文章。 SD2.0 Specification简述 CRC全称为Cyclic Redundancy Code&#xff0c;中文名称是循环冗余校验&#xff0c;该方法通过附加冗余数据来保证数据的完整性&#xff0c;即用于检…

一类医疗器械产品分类目录 2002版

医疗医疗器械备案申请时&#xff0c;需要填写老的分类目录表&#xff08;2022版&#xff09; 在网上找了很多&#xff0c;都没有Word的&#xff0c;于是自己到官网上整理了一份分享给用到的朋友&#xff01; 下载地址&#xff1a; 一类医疗器械产品分类目录2002版资源-CSDN…

latex作者介绍添加,以及作者介绍段落间距调整(看这篇就够了)

文章目录 1.latex语句如何添加作者的介绍和照片2.作者介绍段落和段落之间的距离太大如何调整 1.latex语句如何添加作者的介绍和照片 \begin{IEEEbiography}[{\includegraphics[width1in,height1.25in,clip,keepaspectratio]{图像存放地址}}]{作者姓名} 这里写作者介绍 \end{IE…

Qt --- 界面优化 --- QSS和绘图API

界面优化 》美化 一个程序的界面是否好看&#xff0c;是否重要呢。 有些面向专业领域的程序&#xff0c;界面好看与否&#xff0c;不是看关键&#xff0c;更关键的是实际的效果。有些面向普通用户领域的程序&#xff0c;界面好看&#xff0c;还是很大的加分项。 界面优化 Qt…

基于单片机的多路温度检测系统

**单片机设计介绍&#xff0c;基于单片机CAN总线的多路温度检测系统设计 文章目录 前言概要功能设计设计思路 软件设计效果图 程序设计程序 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探…

【Vue3】组件元素定义的ref属性和expose,setup返回值的关系

1、实例代码 首先一个测试结果: 1、在组件模版中元素定义的ref属性&#xff0c;会在编译阶段生成对应的props数据&#xff0c;作为参数传入到创建这个ref所在元素的vnode的方法中。 2、如果ref定义在一般的元素中&#xff0c;那么ref就指向这个元素的dom实例&#xff0c;如果re…