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[])
{//initstrcpy(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));		//写入当前进程pidprintf("当前pid:%d\n",pid);int fd2 = open(argv[2],O_RDWR); 		//读管道read(fd2,&pid,sizeof(pid_t));		//读出要通信的pidclose(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,如果等待成功,//立即把信号量的值置为 0struct 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.rhkb.cn/news/193486.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

web缓存-----squid代理服务

squid相关知识 1 squid的概念 Squid服务器缓存频繁要求网页、媒体文件和其它加速回答时间并减少带宽堵塞的内容。 Squid代理服务器&#xff08;Squid proxy server&#xff09;一般和原始文件一起安装在单独服务器而不是网络服务器上。Squid通过追踪网络中的对象运用起作用。…

【C语言 | 指针】指针和数关系——剪不断,理还乱

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

算法学习打卡day45|动态规划:股票问题总结

Leetcode股票问题总结篇 动态规划的股票问题一共六道题&#xff0c;买卖股票最佳时机和买卖股票手续费都是一个类型的问题&#xff0c;维护好买入和卖出两个状态即可&#xff0c;方法一摸一样。而冷冻期也差不多就是状态多了点&#xff0c;买入、保持卖出、当日卖出、以及冷冻期…

Android10 手势导航

种类 Android10 默认的系统导航有三种&#xff1a; 1.两个按钮的 2.三个按钮的 3.手势 它们分别对应三个包名 frameworks/base/packages/overlays/NavigationBarMode2ButtonOverlay frameworks/base/packages/overlays/NavigationBarMode3ButtonOverlay frameworks/base/packa…

基于安卓android微信小程序的快递取件及上门服务系统

项目介绍 本文从管理员、用户的功能要求出发&#xff0c;快递取件及上门服务中的功能模块主要是实现管理员服务端&#xff1b;首页、个人中心、用户管理、快递下单管理、预约管理、管理员管理、系统管理、订单管理&#xff0c;用户客户端&#xff1b;首页、快递下单、预约管理…

笔记51:循环神经网络入门

本地笔记地址&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\3.循环神经网络\循环神经网络 a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a

VS2017新建.hpp文件

目录 1、新建h文件的方法&#xff1a;2、新建对用的cpp文件&#xff1a;3、在main.cpp中调用 1、新建h文件的方法&#xff1a; 2、新建对用的cpp文件&#xff1a; 3、在main.cpp中调用 参见大佬博客

【flink实战】动态表:关系查询处理流的思路:连续查询、状态维护;表转换为流需要编码编码

文章目录 一. 使用关系查询处理流的讨论二. 动态表 & 连续查询(Continuous Query)三. 在流上定义表1. 连续查询2. 查询限制2.1. 维护状态2.2. 计算更新 四. 表到流的转换1. Append-only 流2. Retract 流3. Upsert 流 本文主要讨论了&#xff1a; 讨论通过关系查询处理无界流…

天津专升本新版报名系统网上报名、填志愿、缴费、审核等操作步骤

天津高职升本网上报名、填报志愿新版专升本报名系统 ▏报名入口&#xff1a;www.zhaokao.net▏注意&#xff1a;一定要在截止时间内完成报名、填报志愿、缴费、审核、下载《报名信息表》等4步骤▏可报考院校及专业请参考招生院校发布的通知&#xff08;招生简章、报考须知&…

YOLOv7独家原创改进:最新原创WIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能精度

💡该教程为属于《芒果书》📚系列,包含大量的原创首发改进方式, 所有文章都是全网首发原创改进内容🚀 💡本篇文章为YOLOv7独家原创改进:独家首发最新原创WIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能精度。 💡对自己数据集改进有效…

EMNLP 2023 | 用于开放域多跳推理的大语言模型的自我提示思想链

©PaperWeekly 原创 作者 | 王金元 单位 | 上海交通大学 研究方向 | 大模型微调及应用 论文标题&#xff1a; Self-prompted Chain-of-Thought on Large Language Models for Open-domain Multi-hop Reasoning 模型&代码地址&#xff1a; https://github.com/noewangj…

Android 框架

MVC: MVP MVVM Model 数据以及业务数据 View 视图 Control 控制器 simple code MVP OnFinishInflate ViewGroup 加载完成 MVC 优化 Struts MVC- MVP MVC-单次调用逻辑把 MVP / 把C拆分出来 MVVM 2017Google推出ViewModel DataBind MVVM是一种框架规则,双向绑定 Model…

LeetCode(16)接雨水【数组/字符串】【困难】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 42. 接雨水 1.题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&…

技巧篇:Mac 环境PyCharm 配置 python Anaconda

Mac 中 PyCharm 配置 python Anaconda环境 在 python 开发中我们最常用的IDE就是PyCharm&#xff0c;有关PyCharm的优点这里就不在赘述。在项目开发中我们经常用到许多第三方库&#xff0c;用的最多的命令就是pip install 第三方库名 进行安装。现在你可以使用一个工具来帮你解…

仿京东拼多多商品分类页-(RecyclerView悬浮头部实现、xml绘制ItemDecoration)

文章目录 前言效果图思路方式一&#xff1a;通过xml布局来实现方式二&#xff1a;通过ItemDecoration方式来实现 实现步骤1、数据项格式2、左侧列表适配器3、右侧列表适配器4、头部及悬浮头部绘制4.1头部偏移高度为要绘制xml布局的高度--getItemOffsets()4.2 绘制固定头部--onD…

Leetcode刷题详解——岛屿数量

1. 题目链接&#xff1a;200. 岛屿数量 2. 题目描述&#xff1a; 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上…

RabbitMQ之延迟队列(万字总结,手把手教你学习延迟队列)

文章目录 一、延迟队列概念二、延迟队列使用场景三、RabbitMQ 中的 TTL1、队列设置 TTL2、消息设置 TTL3、两者的区别 四、整合 springboot1、添加依赖2、修改配置文件3、添加 Swagger 配置类 五、队列 TTL1、代码架构图2、配置文件类代码3、消息生产者代码4、消息消费者代码 六…

Java GUI小程序之图片浏览器

以下是一个简单的图片浏览器示例代码&#xff0c;它包含了图片放大缩小、拖拽、上一张/下一张查看等功能。你可以根据它进行扩展&#xff0c;提高用户体验。 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.e…

Linux系统编程——进程中vfork函数

函数原型 pid_t vfork(void);//pid_t是无符号整型 所需头文件 #include <sys/types.h> #include <unistd.h> 功能 vfork() 函数和 fork() 函数一样都是在已有的进程中创建一个新的进程&#xff0c;但它们创建的子进程是有区别的。 返回值 成功子进程中返回 …

如何使用内网穿透实现远程公网访问windows node.js的服务端

使用Nodejs搭建简单的web网页并实现公网访问 前言 Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架。 Node.js自带运行时环境可在Javascript脚本的基础上可以解释和执行(这类似于JVM的Java字节码)。这个运行时允许在浏览器以外的任何机器上执行JavaScri…