Linux C 进程间通信

进程间通信

  • 概述
  • 进程间通信方式
    • 管道
      • 概述
      • 管道函数
        • 无名管道 pipe
        • 有名管道 makefifo
        • 删除有名管道 rmove
      • 有名管道实现 双人无序聊天 例子
    • 信号
      • 信号概述
      • 信号处理过程
      • 信号函数
        • 传送信号给指定的进程 kill
        • 注册信号 signal
        • 查询或设置信号处理方式 sigaction
        • 设置信号传送闹钟 alarm
      • 有名管道+信号实现 双人无序聊天 例子
    • 共享内存
      • 概述
      • 特性
      • 共享内存命令
      • 共享内存函数
        • 创建\打开共享内存 shmget
        • 映射共享内存 shmat
        • 解除映射 shmdt
        • 删除共享内存
    • 消息队列
      • 概述
      • 消息队列函数
        • 创建消息队列 msgget
        • 消息队列发送消息 msgsnd
        • 消息队列接收消息 msgrcv
        • 删除消息队列 msgctl
    • 信号量集
      • 概述
      • 函数
        • 获取\创建信号量 semget
        • 控制信号量 semctl
        • 改变信号量的值 semop
  • 基于进程间通信+多进程编写的银行模拟系统案例

概述

  为什么进程间需要通信?为了传输数据、共享资源、通知事件、控制进程等。
  那么进程间通信的原理是什么呢?进程在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信机制。
在这里插入图片描述

进程间通信方式

  进程间通信主要有七种方式,分别是管道(有名、无名)、信号、共享内存、消息队列、信号量集以及套接字Socket(套接字后面单独介绍)。

管道

概述

  无名管道只能用于父子进程或兄弟进程之间的通信,而有名管道可用于任意两进程之间通信。

管道函数

无名管道 pipe

  无名管道单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存(RAM)中。
  数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

头文件:
  #include<unistd.h>
函数原型:int pipe(int filedes[2]);
参数介绍:
  fd:一个大小为2的一个数组类型的指针。filedes[0]为管道里的读取端,filedes[1]则为管道的写入端。
返回值:若成功则返回零,否则返回-1,错误原因存于 errno 中。
在这里插入图片描述
只有fork函数才能创建的父子进程间才能使用无名管道。

有名管道 makefifo

  命名管道是为了解决无名管道只能用于近亲进程之间通信的缺陷而设计的。命名管道是建立在实际的磁盘介质或文件系统上有自己名字的文件,任何进程可以在任何时间通过文件名或路径名与该文件建立联系。为了实现命名管道,引入了一种新的文件类型——FIFO 文件(遵循先进先出的原则)。实现一个命名管道实际上就是实现一个 FIFO 文件。命名管道一旦建立,之后它的读、写以及关闭操作都与普通管道完全相同。在文件系统中有文件名,有文件节点。
头文件:
  #include<sys/types.h>
  #include<sys/stat.h>
函数原型:int mkfifo(const char * pathname,mode_t mode);
参数介绍:
  pathname:管道创建 路径+名字
  mode:文件权限,与umask有关
返回值:若成功则返回 0,否则返回-1,错误原因存于 errno 中。
在这里插入图片描述
这样任意两个进程可以通过文件IO操作在其中进行数据传输。

删除有名管道 rmove

头文件:
  #include<stdio.h>
函数原型:int remove(const char * pathname);
参数介绍:
  pathname:管道 路径+名字
返回值:若成功则返回 0,否则返回-1,错误原因存于 errno 中。

有名管道实现 双人无序聊天 例子

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
	//创建管道
	int val1 = mkfifo(argv[1],0666);
	int val2 = mkfifo(argv[2],0666);
	//打开管道
	int fd1 = open(argv[1],O_RDWR);
	int fd2 = open(argv[2],O_RDWR);
	//创建进程
	pid_t pid = fork();
	if( pid > 0 )//父进程
	{
		while(1)
		{
			char wb[512] = {0};
			gets(wb);
			write(fd1,wb,strlen(wb));
		}
	}
	else if( pid == 0 ) //子进程
	{
		while(1)
		{
			char rb[512] = {0};
			read(fd2,rb,sizeof(rb));
			printf("其他:%s\n",rb);
		}
	}
	close(fd1);
	close(fd2);
	return 0;
	//同时在两个终端中运行,形成双管道,四进程
	//父进程负责写入数据,子进程负责读出数据
	//因为read和write都是阻塞的,故可以一直等待到数据变化
}

信号

信号概述

  在Linux中使用 kill -l 命令可以查看到x 系统中有下 62 个信号,每一个信号都有自己独特的含义。前 31 个信号继承 unix 的非实时信号,后 31 个是 linux 自己扩展的实时信号,没有固定的含义(或者说可以由用户自由使用),所有的实时信号的默认动作都是终止进程。每一个信号用一个整型常量宏(信号编号)表示,以“SIG”开头,在系统头文件<signal.h>中定义。
在这里插入图片描述

信号处理过程

1 ) 信号的发生 ------ 内核进程能够发送信号(产生中断)。
2 ) 信号的接收 ------ 用户进程接收信号(保证进程不结束)。
3 ) 信号的处理 ------ 中断服务函数(信号服务函数)。

信号函数

传送信号给指定的进程 kill

头文件:
  #include<sys/types.h>
  #include<signal.h>
函数原型:int kill(pid_t pid,int sig);
参数介绍:
  pid:目标进程pid
  sig:要发送的信号(数字)
返回值:执行成功则返回 0,如果有错误则返回-1。

	kill(atoi(argv[1]),atoi(argv[2]));
注册信号 signal

头文件:
  #include<signal.h>
函数原型:void (signal(int signum,void( handler)(int)))(int);
即  typedef void (*sighandler_t)(int); //函数指针类型
   sighandler_t signal(int signum, sighandler_t handler);
参数介绍:
  下面的例子会然你看懂的
返回值:执行成功则返回 0,如果有错误则返回-1。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
//信号服务函数
void signal_function(int num)
{
	printf("2 号信号:%d\n",num);
}
int main(int argc,char *argv[])
{
	//注册信号
	sighandler_t val = signal(2,signal_function);
	while(1)return 0;
}

ctrl+c 信号是2号信号,所以当按下 ctrl+c 时服务函数 signal_function() 就会输出。

在这里插入图片描述

查询或设置信号处理方式 sigaction

头文件:
  #include<signal.h>
函数原型:int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
参数介绍:
  signum:要捕获的信号类型
  act:指定新的信号处理方式
  oldact:输出先前信号的处理方式
返回值:执行成功则返回 0,如果有错误则返回-1。
参数结构体 sigaction 定义如下:
struct sigaction
{
  void (*sa_handler) (int); //代表新的信号处理函数
  sigset_t sa_mask; //设置在处理该信号时暂时将 sa_mask 指定的信号搁置。
  int sa_flags; //用来设置信号处理的其他相关操作
  void (*sa_restorer) (void);// 此参数没有使用
}

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
void signal_function(int num)
{
	printf("2 号信号:%d\n",num);
}
int main(int argc,char *argv[])
{
	//注册信号
	struct sigaction act;
	act.sa_handler =signal_function;//信号服务函数
	sigemptyset(&act.sa_mask); //初始化信号集
	sigaddset(&act.sa_mask,2); //添加搁置信号
	sigaddset(&act.sa_mask,3); //添加搁置信号
	act.sa_flags =0;
	sigaction(2,&act,NULL);
	while(1);
	return 0;
	//运行结果图和注册信号代码运行结果图一样
	//当按下 ctrl+c 时服务函数signal_function()就会输出。
}
设置信号传送闹钟 alarm

头文件:
  #include<unistd.h>
函数原型:unsigned int alarm(unsigned int seconds);
参数介绍:
  seconds:经过指定的秒数后发送信号给当前进程。为 0,则之前设置的闹钟会被取消,并将剩下的时间返回。
返回值:返回之前闹钟的剩余秒数,如果之前未设闹钟则返回 0。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
typedef void (*sighandler_t)(int);
extern int errno;
void signal_function(int num)
{
	printf("14 号信号:%d\n",num);
}
int main(int argc,char *argv[])
{
	//注册信号
	struct sigaction act;
	act.sa_handler =signal_function;//信号服务函数
	sigemptyset(&act.sa_mask); //初始化信号集
	sigaddset(&act.sa_mask,2); //添加搁置信号
	sigaddset(&act.sa_mask,3); //添加搁置信号
	act.sa_flags =0;
	sigaction(14,&act,NULL);
	
	printf("%d\n",alarm(10));
	sleep(2);
	printf("%d\n",alarm(5));
	
	while(1);
	return 0;
}

在设置完10秒之后,经过两秒的睡眠还剩8秒,这时候被重新设置为五秒(上次计时还剩8秒),五秒后14号信号被发出并接收显示。
在这里插入图片描述

有名管道+信号实现 双人无序聊天 例子

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
char path[10];
//信号服务函数
void signal_fun(int num)
{
	int fd2 = open(path,O_RDWR);		//打开读管道
	char rb[512] = {0};
	read(fd2,rb,sizeof(rb));	
	printf("%d说:%s\n",num,rb);
	close(fd2);							//关闭读管道	
}
//关于argv:
// 1 是读通道  2 是写通道  3 是自己的信号  4 是发的信号
int main(int argc,char *argv[])
{
	//init
	strcpy(path,argv[2]);					//保证信号服务函数能打开对应的管道
	pid_t pid = getpid();					//或取自己的pid
	//创建管道
	int val1 = mkfifo(argv[1],0666);
	int val2 = mkfifo(argv[2],0666);
	//打开管道
	int fd1 = open(argv[1],O_RDWR); 		//写管道
	write(fd1,&pid,sizeof(pid_t));		//写入当前进程pid
	printf("当前pid:%d\n",pid);
	int fd2 = open(argv[2],O_RDWR); 		//读管道
	read(fd2,&pid,sizeof(pid_t));		//读出要通信的pid
	close(fd2);
	printf("通信pid:%d\n",pid);
	//注册信号
	sighandler_t val = signal(atoi(argv[3]),signal_fun);
	//等待写入
	while(1)
	{
		char wb[512] = {0};
		gets(wb);
		write(fd1,wb,strlen(wb));
		kill(pid,atoi(argv[4]));
	}
	close(fd1);
	return 0;
	//同时在两个终端中运行,形成双管道,双进程
	//主函数写入管道数据并发送信号到另一个进程
	//信号服务函数在接收到信号之后读取管道内容
}

共享内存

概述

  指同一块物理内存被映射到进程 A、B 进程地址空间中。进程 A 可以即时看到进程 B 对共享内存中数据的更新。

特性

  1. 数据传输效率快,适用于对数据的速率、数量要求较高的场合(如果要求不高,一般使用消息队列)。
  2. 共享内存具有内存的通用特性,对共享内存执行写操作时以覆盖的方式写入,对共享内存读取数据后,内存中的数据保留,不会删除。
  3. 内核中的内存是不具有共享机制的,在使用共享内存前需要先创建一块共享内存(物理内存)。
  4. 共享内存并未提供同步机制,在一个进程结束对共享内存的写操作之前,不可以使用另外一个进程开始对它进行读取。

共享内存命令

查看内核中 IPC 对象:
  ipcs -m 共享内存
     -q 消息队列
     -s 信号灯
删除内核中 ipc 对象:
  ipcrm -m id号

共享内存函数

创建\打开共享内存 shmget

头文件:
  #include <sys/ipc.h>
  #include <sys/shm.h>
函数原型:int shmget(key_t key, size_t size, int shmflg);
参数介绍:
  key:共享内存标识符
  size:创建共享内存空间大小
  shmflg:权限标志
返回值:成功返回共享内存的标识符。失败,则返回 -1,并设置 errno 来指示错误类型。

	int shmid = shmget(0x6666,512,IPC_CREAT|0644);
映射共享内存 shmat

头文件:
  #include <sys/types.h>
  #include <sys/shm.h>
函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数介绍:
  shmid:共享内存的标识符。
  shmaddr:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
  shmflg:是一组标志位,通常为0。
返回值:调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

	char * pos = (char *)shmat(shmid,NULL,0);
解除映射 shmdt

头文件:
  #include <sys/types.h>
  #include <sys/shm.h>
函数原型:int shmdt(const void *shmaddr);
参数介绍:
  shmaddr:指定共享内存连接到当前进程中的地址位置。
返回值:成功返回 0,失败返回-1。

	shmdt(shmid);
删除共享内存

头文件:
  #include <sys/ipc.h>
  #include <sys/shm.h>
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数介绍:
  shmid:共享内存标识。
  cmd:采取的操作,具体如下:
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值。
    IPC_RMID:删除共享内存段。
  buf:指向共享内存模式和访问权限的结构。
返回值:成功返回 0,失败返回-1。
如果需要删除共享内存,必须保证共享内存的所有连接全部断开(取消映射)后才能被真正删除。

	int val = shmctl(shmid,IPC_RMID,NULL);

消息队列

概述

  消息通信方式以消息缓冲区为中间介质,通信双方的发送和接收操作均以消息为单位。在存储器中,消息缓冲区被组织成队列,通常称之为消息队列。
  消息队列是消息(消息内容及消息类型)的链表,存放在内核中并由消息队列标识符表示。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出,但是对于同一消息类型为先进先出。
  消息队列提供了一个从一个进程向另一个进程发送数据块(结构体)的方法,每个数据块都可以被认为是有一个类型,接受者接受的数据块可以有不同的类型,接收进程根据不同类型数据进行选择接收。

消息队列函数

创建消息队列 msgget

头文件:
   #include <sys/types.h>
   #include <sys/msg.h>
   #include <sys/ipc.h>
函数原型:int msgget(key_t key, int msgflg);
参数介绍:
  key:消息队列的标识符
  msgflg:权限标志,具体如下
    IPC_CREAT //如果key不存在,则创建(类似open函数的O_CREAT)
    IPC_EXCL //如果key存在,则返回失败(类似open函数的O_EXCL)
    IPC_NOWAIT //如果需要等待,则直接返回错误
返回值:成功执行时,返回消息队列标识符。失败返回-1。

	int msgid = msgget(0x6666, IPC_CREAT|0666);
消息队列发送消息 msgsnd

头文件:
  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/msg.h>
函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数介绍:
  msqid:消息队列的标识符。
  msgp:指向要发送的消息所在的内存。
  msgsz:消息的长度。
  msgflg:是控制函数行为的标志,通常为0。
返回值:错误时返回-1,可以打印错误信息。正确返回 0。

struct msgbuf 
{
	long mtype; /* message type, must be > 0 */
	char mtext[512]; /* message data */
	int id;
};

	struct msgbuf info;
	int val = msgsnd(msgid,&info,sizeof(struct msgbuf)-sizeof(long),0)
消息队列接收消息 msgrcv

头文件:
  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/msg.h>
函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数介绍:
  msqid:消息队列的标识符。
  msgp:指向要发送的消息所在的内存。
  msgsz:消息的长度。
  msgtyp:如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。
  msgflg:是控制函数行为的标志,通常为0。
返回值:

	struct msgbuf recvinfo;
	ssize_t len = msgrcv(msgid,&recvinfo,sizeof(struct msgbuf)-sizeof(long), 1,0);
删除消息队列 msgctl

头文件:
  #include <sys/types.h>
   #include <sys/ipc.h>
  #include <sys/msg.h>
函数原型:int msgctl ( int msgqid, int cmd, struct msgid_ds *buf );
参数介绍:
  msqid:消息队列的标识符。
  cmd:将要采取的动作,它可以取3个值:
    IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
    IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值。
    IPC_RMID:删除消息队列。
  buf :指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。
返回值:成功时返回0,失败时返回-1。

	msgctl (msgid, IPC_RMID, NULL);

信号量集

概述

  信号量集是由多个信号量组成的一个数组,作为一个整体,信号量集中的所有信号量使用同一个等待队列。Linux 的信号量集为进程请求多个资源创造了条件。Linux 规定,当进程的一个操作需要多个共享资源时,如果只成功获得了其中的部分资源,那么这个请求即告失败,进程必须立即释放所有已获得资源,以防止形成死锁。

函数

获取\创建信号量 semget

头文件:
  #include <sys/ipc.h>
  #include <sys/sem.h>
  #include <sys/types.h>
函数原型:int semget(key_t key, int nsems, int semflg);
参数介绍:
  key:信号量的键值
  nsems:创建信号量的个数,大多数情况为1。
  semflg:是一组标志,如果希望信号量不存在时创建一个新的信号量,可以和值 IPC_CREAT 做按位或操作。如果没有设置 IPC_CREAT标志并且信号量不存在,就会返错误(errno 的值为 2,No such file or directory)。
返回值:返回信号量集的标识;失败返回-1,错误原因存于 error 中。

	//获取键值为 0x5000 的信号量
	//如果该信号量不存在,就创建它
	int semid = semget(0x5000,1,0640|IPC_CREAT);
控制信号量 semctl

头文件:
  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/sem.h>
函数原型:int semctl(int semid, int sem_num, int command, …);
参数介绍:
  semid:信号量标识
  sem_num:是信号量集数组下标
  command:对信号量操作的命令,具体如下:
    IPC_RMID:销毁信号量,不需要第四个参数;
    SETVAL:用来把信号量初始化为一个已知的值。
返回值:失败返回-1;如果成功,返回值比较复杂,想了解的自行搜索。
用于信号操作的共同体:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};

	//销毁信号量。
	semctl(semid,0,IPC_RMID);
	//初始化信号量的值为 1,信号量可用。
	union semun sem_union;
	sem_union.val = 1;
	semctl(semid,0,SETVAL,sem_union);
改变信号量的值 semop

头文件:
  #include <sys/ipc.h>
  #include <sys/types.h>
函数原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
参数介绍:
  sem_id:信号量标识。
  sops:操作信号量的个数。
  nsops:结构体,具体内容在下面给出。
返回值:成功返回0,失败返回-1,错误原因存于 error 中。
nsops结构体:
struct sembuf
{
short sem_num; // 信号量序号,单个信号量设置为 0。
short sem_op; //信号量操作,-1 等待操作;1 发送操作。
short sem_flg; //把此标志设置为 SEM_UNDO,操作系统将跟踪这个信号量。
};

	//等待信号量的值变为 1,如果等待成功,
	//立即把信号量的值置为 0
	struct sembuf sem_b;
 	sem_b.sem_num = 0;
 	sem_b.sem_op = -1;
 	sem_b.sem_flg = SEM_UNDO;
 	semop(sem_id, &sem_b, 1);
 	//把信号量的值置为 1。
	struct sembuf sem_b;
 	sem_b.sem_num = 0;
 	sem_b.sem_op = 1;
 	sem_b.sem_flg = SEM_UNDO;
 	semop(sem_id, &sem_b, 1);

基于进程间通信+多进程编写的银行模拟系统案例

点我~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ONE·Linux || 网络基础(三)】

总言 主要内容&#xff1a;HTTP和HTTPS工作方式简述。 文章目录 总言6、HTTP协议&#xff08;应用层二&#xff09;6.1、入门认识6.1.1、认识URL6.1.2、urlencode和urldecode 6.2、快速构建6.2.1、快速构建http请求和响应的报文格式6.2.2、http demo6.2.2.1、sock.hpp &&a…

vite环境变量相关

环境变量&#xff1a;根据环境的不同&#xff0c;灵活的自动读取相应的变量。避免了手动修改。 import path from path import postCssPxToRem from postcss-pxtorem import { defineConfig, loadEnv } from vite import createVitePlugins from ./vite/plugins import copy f…

【LeetCode刷题笔记】二叉树(三)

701. 二叉搜索树中的插入操作 解题思路: 1. 模拟 ,如果 根节点为空 ,就用 插入值创建根节点 直接返回。否则, cur 从 根节点 开始,比较 当前节点的值和插入值的大小关系 : 1)如果 插入值 < cur ,就

一张图系列 - “position_embedding”

关于位置编码&#xff0c;我感觉应该我需要知道点啥&#xff1f; 0、需要知道什么知识&#xff1f; multi head atten 计算 复数的常识 1、embedding 是什么&#xff1f; position embedding常识、概念&#xff0c;没有会怎样&#xff1f; 交换token位置&#xff0c;没有P…

vue手动搭建脚手架(保姆式教案)

目录 1.创建项目 1.node.js环境搭建 2.安装vue-cli 3.搭建项目 目录结构 1.创建项目 1.node.js环境搭建 下载安装node.js&#xff08;Download | Node.js&#xff09;&#xff0c;安装时不要安装在C盘Windowsr打开cmd管理工具开始输入命令检查node.js是否安装和版本号&a…

在IDEA中的DeBug调试技巧

一、条件断点 循环中经常用到这个技巧&#xff0c;例如&#xff1a;遍历1个List的过程中&#xff0c;想让断点停在某个特定值。 参考上图&#xff0c;在断点的位置&#xff0c;右击断点旁边的红点&#xff0c;会出来1个界面&#xff0c;在Condition这里填写断点条件即可&#…

QCustomPlot的下载和使用

0.QCustomPlot介绍 QCustomPlot是一个基于Qt画图和数据可视化的C控件。在Qt下的绘图工具有Qwt、QChart和QCustomPlot&#xff0c;置于选择哪个绘图工具各有优缺点。 在绘制大量数据&#xff08;10万个点以上&#xff09;时选择QCustomPlot&#xff0c;在数据量比较小时&#x…

docker内更新显卡cuda cudnn

当前docker使用的cuda为10.2&#xff0c;为保证服务器环境使用相同的cuda版本&#xff0c;需对cuda版本进行升级&#xff0c;时间长了忘记如何操作&#xff0c;此处记录一下&#xff1a; *docker内使用的cuda版本低于容器外的显卡驱动版本即可&#xff0c;此处不对显卡驱动进行…

解决STM32F429烧录程序后还需复位才能植入程序的bug

1.打开魔术棒&#xff0c;打开debug 2.打开setting 3.打开Flas Download 4.开启Reset and Run 5.点进去Pack选项页面&#xff0c;去掉enable

springboot 2.1.0.RELEASE 项目加入swagger接口文档

Release v2.1.0.RELEASE spring-projects/spring-boot GitHub springboot 2.1.0.RELEASE发行日期是2018年10月30日&#xff08;Oct 30, 2018&#xff09; 不要使用过高的swagger版本&#xff0c;如SpringFox Boot Starter 3.0.0&#xff0c;否则报错&#xff1a; spring-…

MOS管体电极接源端版图layout画法

记录一个lvs一直跑不通的问题。 问题描述&#xff1a;lvs一直显示某几个MOS管的体电极连接问题。连线没有问题&#xff0c;版图中已经画了衬底。 原因&#xff1a; 图中四个管子的衬底接了源端&#xff0c;没接电源。 解决办法&#xff1a; 法1、源端接地 法2、将这四个管子…

msvcp140.dll丢失的解决办法,msvcp140.dll丢失会导致电脑出现哪些错误

msvcp140.dll丢失的解决办法都有哪些&#xff1f;如果电脑不及时将msvcp140.dll文件进行修复的话电脑可能会出电脑可会出现哪些错误&#xff1f;今天就和大家说说都有哪些解决办法有效的解决msvcp140.dll丢失的问题。 一.缺失msvcp140.dll会有什么问题 当电脑上缺少msvcp140.d…

67基于matlab图像处理,包括颜色和亮度调整、翻转功能、空间滤波和去噪、频域滤波和去噪、噪声添加,形态学操作、边缘检测及示波器集成的GUI图像处理。

基于matlab图像处理&#xff0c;包括颜色和亮度调整、翻转功能、空间滤波和去噪、频域滤波和去噪、噪声添加&#xff0c;形态学操作、边缘检测及示波器集成的GUI图像处理。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 67 matlab图像处理图像降噪 (xiaohon…

Python Web APP在宝塔发布

本地测试运行&#xff1a;uvicorn main:app --host 127.0.0.1 --port 8082 --reload 宝塔发布&#xff1a; 运行配置——>启动模式&#xff1a;worker_class uvicorn.workers.UvicornWorker

stable diffusion到底是如何工作的

stable diffusion简单入门 stable diffusion是一个文生图模型&#xff0c;主要由CompVis、Stability AI和LAION的研究者们创建。这个模型主要是在512X512分辨率的图像上训练的&#xff0c;训练数据集是LAION-5B&#xff0c;该数据集是目前可访问的最大的多模态数据集。 在这篇…

Spring6(四):JUnit、事务

文章目录 5. 单元测试&#xff1a;JUnit5.1 整合JUnit55.2 整合JUnit4 6. 事务6.1 JdbcTemplate6.1.1 准备工作6.1.2 实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返回对象④查询数据返回list集合⑤查询返回单个的值 6.2 事务6.2.1 编程式事务6.2.2 声明式事务 6.3 …

【数据结构】直接选择排序(你知道最不常用的排序算法有哪些吗?)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…

AI创作系统ChatGPT网站源码+详细搭建部署教程+支持DALL-E3文生图/支持最新GPT-4-Turbo-With-Vision-128K多模态模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

OpenSign:安全可靠的电子签名解决方案 | 开源日报 No.76

microsoft/Web-Dev-For-Beginners Stars: 71.5k License: MIT 这个开源项目是一个为期 12 周的全面课程&#xff0c;由微软云倡导者团队提供。它旨在帮助初学者掌握 JavaScript、CSS 和 HTML 的基础知识。每一节都包括预习和复习测验、详细的书面指南、解决方案、作业等内容。…

JavaScript学习_01——JavaScript简介

JavaScript简介 JavaScript介绍 JavaScript是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供…