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.hqwc.cn/news/178697.html

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

相关文章

Python爬取股票交易数据代码示例及可视化展示。

文章目录 前言一、开发环境二、第三方模块三、爬虫案例步骤四、爬虫程序全部代码1.分析网页2.导入模块3.请求数据4.解析数据5.翻页6.保存数据 五、实现效果六、数据可视化全部代码1.导入数据2.读取数据3.可视化图表4.效果展示关于Python技术储备一、Python所有方向的学习路线二…

Jenkins的一些其他操作

Jenkins的一些其他操作 1、代码仓库Gogs的搭建与配置 Gogs 是一款极易搭建的自助 Git 服务&#xff0c;它的目标在于打造一个最简单、快速和轻松的方式搭建 Git 服务。使用 Go 语言开发的它能够通过独立的二进制进行分发&#xff0c;支持了 Go 语言支持的所有平台&#xff0…

企业计算机中了eking勒索病毒如何解毒,eking勒索病毒文件恢复

网络技术的不断发展&#xff0c;为企业的生产生活提供了极大便利&#xff0c;但随之而来的网络安全威胁也不断增加&#xff0c;近期&#xff0c;很多企业的计算机服务器遭到了eking勒索病毒攻击&#xff0c;导致企业的计算机服务器所有数据被加密&#xff0c;无法正常使用&…

2024年度“阳江市惠民保”正式发布!阳江市专属补充医疗保险全新升级

11月14日&#xff0c;2024年度“阳江市惠民保”暨百场义诊活动发布会在阳江市华邑酒店顺利举行。2024年度“阳江市惠民保”一年保费最低只要59元&#xff0c;最高可获得400万元的医疗保障。 阳江市人民政府、阳江市医疗保障局、阳江市农业农村局、阳江市金融工作局、国家金融监…

使用flutter的Scaffold脚手架开发一个最简单的带tabbar的app模板

flutter自带的scaffold脚手架可以说还是挺好用的&#xff0c;集成了appBar&#xff0c;还有左侧抽屉&#xff0c;还有底部tabbar&#xff0c;可以说拿来就可以用了啊&#xff0c;所以我今天也体验了一下&#xff0c;做了一个最简单的demo&#xff0c;就当是学习记录了。 效果展…

干货分享---- 金融贷款电销获客的方法、渠道

电话营销的现状是&#xff0c;它过去使用电话资源在常规交易平台上正常工作&#xff0c;但进入时&#xff0c;对方总是挂断电话&#xff0c;甚至被他人标记为骚扰&#xff0c;这使工作变得困难。事实上&#xff0c;电话营销交易量飙升的关键很简单&#xff0c;那就是营销技巧和…

SpringCloud集成Eureka并实现负载均衡

文章目录 前言1.Eureka2.整合Eureka作为注册中心2.1创建maven工程2.2依赖配置(Eureka-Server)2.3业务配置(Eureka-Client)2.4服务调用流程 3.集成负载均衡 前言 辗转两家公司也算工作了大半年&#xff0c;有幸见识过很多微服务架构&#xff0c;比如DubboRedis的组合&#xff1…

香港优才计划需要什么条件?2023申请条件/流程/政策放宽!

香港优才计划需要什么条件&#xff1f;2023申请条件/流程/政策放宽&#xff01; 香港优才计划全称&#xff1a;“香港优秀人才入境计划”&#xff0c;是香港特区政府为了吸引优秀人才来港定居&#xff0c;借以提升香港的国际竞争力&#xff0c;于2006年6月正式推行的。 2022年1…

2年博士后|心外医生赴美国耶鲁大学开展研究

G医生决定放弃申报CSC&#xff0c;改为自费出国&#xff0c;并在美国密歇根大学安娜堡分校和耶鲁大学两所名校中选择了更为出名的后者。因为不是CSC出资&#xff0c;G医生得以通过签证&#xff0c;顺利出国&#xff0c;实现了在世界知名高校从事2年博士后的个人职业规划目标。 …

lc121. 买卖股票的最佳时机

一次遍历&#xff0c;一边遍历一边修改买入的价格&#xff0c;一边比较取得最大利润 public class BuyAndSellStocks {public static void main(String[] args) {int[] arr {7,1,5,3,6,4};int[] arr1 {7,6,4,3,1};System.out.println(buyAndSellStocks(arr));System.out.pri…

IDEA从Gitee拉取代码,推送代码教程

打开IDEA&#xff0c;选择Get from Version Control 输入Gitee 仓库项目的URL地址 URL地址输入后点击Clone&#xff0c;即拉取成功 向Gitee提交推送代码 右键选中项目&#xff0c;选中Git 第一步先点击 Add 第二步 点击Commit填写提交信息&#xff0c;点击Commit就会出现下面…

蚂蚁集团首次披露数字科技业务海外发展数据 营收规模同比增长300%

蚂蚁集团资深副总裁、数字科技事业群总裁蒋国飞15日在新加坡金融科技节期间表示&#xff0c;过去一年&#xff0c;蚂蚁数科通过科技产品带来的海外营收规模增长了 300%&#xff0c;其中尤以菲律宾、马来西亚、印度尼西亚等新兴市场国家为主。这是蚂蚁集团首次披露数字科技业务板…