Linux进程通信补充——System V通信

news2025/2/25 0:20:04

System V进程通信

​ System V是一个单独设计的内核模块;

​ 这套标准的设计不符合Linux下一切皆文件的思想,尽管隶属于文件部分,但是已经是一个独立的模块,并且shmid与文件描述符之间的兼容性做的并不好,网络通信使用的是文件的接口,所以System V标准慢慢地边缘化了,很难被整合进网络结构当中;

​ 不同进程看到同一份资源都是使用的key匹配的方式实现;

1.共享内存

​ mmap函数也是一种共享内存,只不过是进程和磁盘之间的通信,这样就不需要使用read和write接口来拷贝,进程结束后,直接将数据刷新到磁盘;

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
/*
addr:映射的起始地址,通常设置为NULL,由系统自动分配。
length:映射的长度,以字节为单位。
prot:映射区域的保护方式,可以是PROT_READ读、PROT_WRITE写、PROT_EXEC可执行、PROT_NONE不可访问等。
flags:映射区域的标志,可以是MAP_SHARED、MAP_PRIVATE等。
fd:要映射的文件描述符。
offset:文件中的偏移量。
*/
//返回值是一个虚拟地址,有语言层直接使用;
使用:
	1.打开磁盘中要映射的文件open;
	2.获取文件的大小ftell;
	3.void *map = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	4.使用map地址;
	5.解除映射,munmap(map, length);
	6.关闭文件;
1.1原理

​ 由操作系统在内存中创建一段连续的空间,经过页表映射到共享区,给需要通信的用户层返回此空间的起始地址;这样就使得不同的进程看到同一份资源;

​ 通信准备工作,第一步申请空间;第二步将空间挂接到进程的共享区;

​ 通信结束工作,第一步去去关联;第二步释放共享内存;

​ 以上过程因为进程的独立性,必须交由操作系统来完成,需要使用系统调用接口;而且操作系统要对共享内存进行管理,有专门的结构体进行描述,憨厚用数据结构进行维护;

1.2系统调用接口
//1.创建一个共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);//创建共享内存的时候也要进行权限设计,即第三个参数设计IPC_CREAT|IPC_EXCL|0666
//第一个参数,操作系统要保证管理的每一个共享内存是唯一的;所以用key(具备唯一性的标识符)来创建共享内存并且使用key进行配对共享内存,来达到获取或者释放;key存放于操作系统创建的描述对象中;
//第二个参数是创建的空间大小。单位是字节,一般是4096的整数倍;
//第三个参数是共享内存的选项,IPC_CREAT and IPC_EXCL,IPC_EXCL不能单独使用;第三个选项一般有两个使用方式;方式一:IPC_CREAT,如果申请的共享内存不存在就创建,存在就获取;方式二:IPC_CREAT|IPC_EXCL,如果申请的共享内存不存在就创建,存在就出错返回;这种方式是为了保证刚创建的共享内存是新的;
//返回值是一个有效的共享内存标识符shmid,是在进程进行唯一性标识的;而key是操作系统使用的,进行唯一性标定的,返回给进程一个shmid;shmid其实就是一个数组,但是不符合文件的设计;
//2.释放共享内存,获取共享内存信息
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
struct shmid_ds {
    struct ipc_perm shm_perm;    /* Ownership and permissions */共享内存的权限
        size_t          shm_segsz;   /* Size of segment (bytes) */
    time_t          shm_atime;   /* Last attach time */
    time_t          shm_dtime;   /* Last detach time */
    time_t          shm_ctime;   /* Last change time */
    pid_t           shm_cpid;    /* PID of creator */
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};//此结构是内核中管理共享内存的子集;
//如下是权限的结构,key被保存在了此结构中
struct ipc_perm {
    key_t          __key;    /* Key supplied to shmget(2) */
    uid_t          uid;      /* Effective UID of owner */
    gid_t          gid;      /* Effective GID of owner */
    uid_t          cuid;     /* Effective UID of creator */
    gid_t          cgid;     /* Effective GID of creator */
    unsigned short mode;     /* Permissions + SHM_DEST and
                                           SHM_LOCKED flags */
    unsigned short __seq;    /* Sequence number */
};
Valid values for cmd are:
IPC_STAT(将内核中的属性拷贝到输出型参数)、IPC_RMID(标记共享内存为删除)、IPC_SET(设置属性);
//第一个参数是shmid;第二个参数是具体的控制方式,查看、修改属性;第三个参数是输出型参数,用来获取共享内存的部分属性;
1.3创建key
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
//一套算法生成key并不会进内核进行查找;pathname和proj_id进行了数值计算,形成一个冲突概率率比较小的key;
//使用同一套算法,pathname,proj_id一定会生成同一个key;
//key是进程传递给操作系统,让操作系统生成唯一性的共享内存,或者操作系统用key进行判断是否允许使用共享内存通信;
1.4指令管理操作系统IPC资源
ipcs(ipc show) -m(memory)
#查看共享内存资源使用情况;
ipcrm -m shmid
#释放 shmid共享内存

在这里插入图片描述

1.5共享内存的权限问题
int shmget(key_t key, size_t size, int shmflg);
//创建共享内存的时候也要进行权限设计,即第三个参数设计IPC_CREAT|IPC_EXCL|0666
1.6注意事项

​ 1.共享内存一旦创建好就需要手动去释放,除非内核关闭;

​ 2.一个进程创建共享内存,一个获取使用共享内存;

​ 3.共享内存的大小建议是4096的整数倍;因为操作系统分配内存是按照page的倍数来的,即使申请4097大小,也还是会开辟2*4096的空间,只不过是第二个4096空间只允许使用1的大小;

​ 4.一个进程负责共享内存的申请和释放,另一个只是挂接和去关联就行;

​ 5.如果进程因为异常终止,就会导致共享内存没有释放,内存泄露;

1.7共享内存的挂接和去关联

​ 进程结束会–attach;

​ malloc是开辟的空间往往比申请的空间要大,是因为这部分额外的空间"cookie"记录了对开辟空间的管理信息

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
//和malloc类似,都是返回一个虚拟地址空间,然后触发缺页中断,建立映射;
//将共享内存挂接到进程地址空间的共享区
//第一个参数共享内存标识符;第二个参数表示的是共享区的具体位置,一般是设为NULL;第三个参数可以自己设置权限,也可以设置0,使用共享内存的默认权限;
//返回值是挂接到共享区的具体位置,
int shmdt(const void *shmaddr);
//进程和共享内存去关联,由于操作系统对共享内存进行了管理,知道大小,随意只需要起始地址就可以释放,类似free;
1.8通信

​ 共享内存通信是不需要像管道一样刷新文件到页缓冲区的,因为直接挂接到了虚拟地址空间,所以用户层是直接可以使用的;直接将挂接的地址强转成char*,当成字符串使用,也不需要语言层缓冲区,不需要调用系统调用;每次从起始位置写入和读取;

char *fgets(char *s, int size, FILE *stream);
//读取之后会在最后加\0;
1.9特性

​ 1.没有同步互斥;

​ 2.共享内存是所有进程间通信方式最快的,因为拷贝少,系统调用少;

​ 3.共享内存的数据需要用户自己维护,是最原始的缓冲区;

1.10使用管道对共享内存进行同步和互斥

​ 两个进程1号读,2号写;2号写完后继续使用管道发送提示,而使用了管道的1号进程,就不会立刻读取,而是等先读到管道数据,才继续读取共享内存的数据;即双方使用共享内存通信中间加入管道通信,借助管道的同步和互斥进行通信;

​ 共享内存的通信速度非常快,对于大文件效率高;可以自己实现同步和互斥,也可以借助管道的同步互斥机制,发送提示符实现;

2.信号量和消息队列

2.1消息队列
2.1.1原理

​ 操作系统在内核中管理消息队列,开辟了队列空间,使得通信双方看到同一份资源;而管道和共享内存使用的是文件页缓冲区和内存块;

​ 通信的要求是:1.必须让不同的进程看到同一份资源;2.允许不同的进程向内核发送带类型(区分发送给哪一个进程)的数据块,然后将数据块链入消息队列;即ab两个进程以发送数据块形式;

在这里插入图片描述

2.1.2创建和控制接口
//创建一个消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
//key和msg的选项和共享内存一样;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//msqid和cmd的选项和共享内存的一样
struct msqid_ds {
    struct ipc_perm msg_perm;     /* Ownership and permissions */使用了组合的方式,而不是继承
    time_t          msg_stime;    /* Time of last msgsnd(2) */
    time_t          msg_rtime;    /* Time of last msgrcv(2) */
    time_t          msg_ctime;    /* Time of last change */
    unsigned long   __msg_cbytes; /* Current number of bytes in
                                                queue (nonstandard) */
    msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
    msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
    pid_t           msg_lspid;    /* PID of last msgsnd(2) */
    pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};
The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):
struct ipc_perm {
    key_t          __key;       /* Key supplied to msgget(2) */
    uid_t          uid;         /* Effective UID of owner */
    gid_t          gid;         /* Effective GID of owner */
    uid_t          cuid;        /* Effective UID of creator */
    gid_t          cgid;        /* Effective GID of creator */
    unsigned short mode;        /* Permissions */
    unsigned short __seq;       /* Sequence number */
};
2.1.3发送和读取接口
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//第一个参数是消息队列id,第二个参数是消息队列数据块起始地址,第三个数据是数据块大小,第四个参数默认设置为0,意思是阻塞方式,IPC_NOWAIT非阻塞的方式;
struct msgbuf {
    long mtype;       /* message type, must be > 0 */类型
    char mtext[1];    /* message data */内容,但是大小为1字节,所以需要自己定义一个数据块对象,保证第一个数据是类型,第二个数据是内容;
};
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//第一个参数是从哪一个消息队列获取,第二和第三个参数是输出型参数作为缓冲区,第四个参数是根据类型读取数据块,第5个参数和发送的一样,阻塞和非阻塞;
2.1.4指令管理IPC资源
ipcs -q
//查看消息队列资源
ipcrm -q msqid
//释放消息队列

在这里插入图片描述

​ used-bytes是总字节数,messages是数据块数;

2.2信号量

​ 信号量不是直接进行数据传输的通信,而是实现协调来实现通信;

​ 总结:1.信号量是一把计数器,PV操作是原子的;2.申请访问共享资源的时候必须先申请信号量,成功之后将共享资源保护转变成临界资源,才可以访问;3.二元信号量就是允许资源互斥访问;4.申请信号量就是对资源的预订机制;

2.2.1原理和概念

1.数据不一致问题,需要解决

​ a进程未写入完,b进程直接读取,导致双方收发数据不完整;管道不存在这样的问题,因为有原子性保证和同步互斥;但是共享内存是最原始的缓冲区,没有任何的保护机制;

2.共享资源

​ 两个进程看到的同一份资源是共享资源,如果不加保护就会导致数据不一致问题;解决这种问题需要使用加锁的方式实现;加锁是为了实现互斥访问;默认打开的3个文件流就是共享资源,不搬家任何保护;

3.互斥

​ 互斥访问,任何时刻只允许一个执行流访问共享资源;实现互斥就是将共享资源变成临界资源;共享内存没有保护机制,消息队列使用带类型的数据块进行保护,信号量就是一种对其他资源的保护,本身通过PV操作原子性保证安全,如申请信号量(P操作),计数器–,释放信号量(V操作),计数器++;ATM取款场景,就是一种互斥访问;

4.临界资源

​ 共享的任何时刻只允许一个执行流访问的资源叫做临界资源;管道就是临界资源;一般是内存空间;而共享内存是不加保护的共享资源;语言层使用的主要是内存资源,而CPU的视角下,操作系统管理的进程是运行在CPU上的,是CPU的资源;

5.临界区

​ 即真正访问临界资源的代码;

6.二元信号量

​ 把值只有0、1两态的计数器叫做二元计数器,本质就是一个锁;

​ 即信号量不仅可以实现对一个资源的互斥访问,也可以实现多个资源的互斥访问;

7.原子性问题

​ 两态,做完或者没有做完;

​ --操作转换成汇编,会变成多条指令,在CPU中一条指令的执行是原子的,即至少会做完一条指令,因为任何操作都会转换成多个指令进行,而其他是非原子的,会使得进程切换的时候导致数据不一致问题;

原理:

​ 信号量本质是一个计数器资源,类似于一个整数的计数器,用来描述临界资源数量的多少;是一种对资源的预订机制,资源一旦被预订了,就不允许其他进程访问了;多个共享资源,被预订了(申请信号量)就会导致,共享资源变成临界资源,共享资源减少;

​ 如:存在一块很大的共享资源,内部存放着多个小的共享资源;每一个资源申请信号量变成临界资源,这样就可以使得多个执行流同时访问不同的临界资源,提高并发度;如果不申请信号量,而且错误的导致多个执行流访问同一块共享资源;所以需要通过信号量:1.对共享资源预订;2.使用计数器–,如果为0就表示资源申请完,不能继续申请了;这样就保证了此共享资源是临界资源。

​ 对于信号量:1.申请计数器成功就允许访问资源了;2.不需要直接去使用,是一种预定机制;3.计数器保证了进入大块共享资源的执行流的数量,一个小块只允许一个执行流进入;4.每一个执行流访问共享资源的时候,必须先申请计数器资源;5.信号量是共享资源;

2.2.2创建和控制接口
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
//一次性创建多个信号量,使用数组的形式维护信号量;
//第一个参数是算法生成的key;第二个参数是申请n个计数器;第三个参数是和之前一样IPC_CREAT|IPC_EXCL|0666;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
//第一个参数是信号量标识符;第二个参数是信号量的个数;第三个参数是进行控制的命令,;可变参数传递的是semid_ds结构体;
union semun {
    int              val;    /* Value for SETVAL */将cmd设置成SETVAL,val用来设置信号量的初始值;
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};
struct semid_ds {
    struct ipc_perm sem_perm;  /* Ownership and permissions */
    time_t          sem_otime; /* Last semop time */
    time_t          sem_ctime; /* Last change time */
    unsigned long   sem_nsems; /* No. of semaphores in set */
};

The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):

struct ipc_perm {
    key_t          __key; /* Key supplied to semget(2) */
    uid_t          uid;   /* Effective UID of owner */
    gid_t          gid;   /* Effective GID of owner */
    uid_t          cuid;  /* Effective UID of creator */
    gid_t          cgid;  /* Effective GID of creator */
    unsigned short mode;  /* Permissions */
    unsigned short __seq; /* Sequence number */
};
2.2.3对信号量的设定
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
//第一个参数是数组下标,对信号量进行设定的时候传入的结构体对象;
struct sembuf{
    unsigned short sem_num;  /* semaphore number */信号量的编号,相当于数组的下标
    short          sem_op;   /* semaphore operation */-1/1,P/V操作;
    short          sem_flg;  /* operation flags */操作选项,自动退出或者非阻塞方式进行;
}

3.System V在内核中的数据结构设计

​ 在操作系统内所有的进程间通信IPC资源被i整合在IPC模块当中;

在这里插入图片描述

​ 上述结构里的entries指针指向了一个数组

在这里插入图片描述

​ 数组的第一个元素表示元素的个数,第二个元素是kernel_ipc_perm对应的指针数组,是一个柔性数组,可以动态扩展数组的大小;

在这里插入图片描述

​ 在操作系统中System V的三种通信方式,都创建了内核结构,这些内核结构的第一个属性都是struct ipc_perm的结构对象,然后使用数组的方式进行组织,而这个数组是struct ipc_perm *array[],存放了这三种内核结构的第一个属性的地址,这样就间接地将三类结构的首地址组织起来了,形成了类似二维数组的结构;

​ 即不同通信方式的描述不同,组织方式转化成了对数组的增删查改;

​ 当每一个进程传入key在操作系统内部进行比对时,进行创建或者获取,不存在创建一个结构,返回数组下标即xxxid;即本质上除去不一样的部分,三种通信方式的结构是一样的,统一成了对ipc资源的管理;为了区分三种通信方式的不同,为每一个通信方式增加了自己的属性;

​ 访问不同的通信结构时,使用强制类型转换即可实现访问;

​ kernel_ipc_perm结构里可以添加一种类型标志位,使用枚举常量的方式区分不同的类型;

​ 这个技术就类似于c++当中的多态,基类ipc_perm,其他三种通信结构是派生类,其是这就是c语言的多态;

​ id_ary数组不隶属于任何进程,不像files_struct里面的fd_array是属于task_struct的,是一个独立的模块里的结构,所以与文件描述符的关系不大,不兼容,此模块逐渐地被边缘化了,因为此模块无法和进程相关联,自己能和内存相关联,而文件是既可以和进程关联也可以和内存关联;

​ xxxid数组下标是线性递增的,当变成最大值时,会回绕到0;实际上并不会开辟很大的数组,而且每次数组都是从下标0开始,类似与起始计数器的机制,将数字转化成对应的数组下标;

在这里插入图片描述

消息类型和消息大小就是从如下结构拷贝的;然后使用了双链表的形式维护了消息队列

struct msgbuf {
    long mtype;       /* message type, must be > 0 */类型
    char mtext[1];    /* message data */内容,但是大小为1字节,所以需要自己定义一个数据块对象,保证第一个数据是类型,第二个数据是内容;
};

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

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

相关文章

Linux入门级别命令(下载远程连接工具)

$pwd&#xff08;当前所在位置&#xff0c;显示打印当前工作目录&#xff09;$mkdir 创建目录$cd dir 换个位置&#xff08;进入某一个目录&#xff09;$cd 什么都不加回到最开始的目录$ls当前目录位置下的文件信息&#xff08;列出当前所在位置下有哪些东西&#xff09;$mv移动…

SpringBoot与SpringCloud的版本对应详细版

在实际开发过程中&#xff0c;我们需要详细到一一对应的版本关系&#xff1a;Spring 官方对应版本地址&#xff1a; (https://start.spring.io/actuator/info)&#xff0c;建议用firefox浏览器打开&#xff0c;你会看见格式化好了json信息&#xff1a; 手动记录一些经本人实际…

导师评价网最全整合版

目录 简介 下载地址 打开index.html即可查询。 简介 下载地址 链接&#xff1a;https://pan.baidu.com/s/1QU6PgoF3Fi8NqtaGHzfAuw?pwdoc0s 提取码&#xff1a;oc0s --来自百度网盘超级会员V5的分享

【Frida】06_分析扫雷游戏的数据,显示地雷位置

&#x1f6eb; 系列文章导航 【Frida】 00_简单介绍和使用 https://blog.csdn.net/kinghzking/article/details/123225580【Frida】 01_食用指南 https://blog.csdn.net/kinghzking/article/details/126849567【Frida】 03_初识frida-node https://blog.csdn.net/kinghzking/ar…

python基于django的高校迎新系统 flask新生报到系统

系统的登录界面和业务逻辑简洁明了&#xff0c;采用一般的界面窗口来登录界面,整个系统更加人性化&#xff0c;用户操作更加简洁方便。本系统在操作和管理上比较容易&#xff0c;还具有很好的交互性等特点&#xff0c;在操作上是非常简单的。因此&#xff0c;本系统可以进行设计…

【代码】YOLOv8标注信息验证

此代码的功能是标注信息验证&#xff0c;将原图和YOLOv8标注文件&#xff08;txt&#xff09;放在同一个文件夹中&#xff0c;作为输入文件夹 程序将标注的信息还原到原图中&#xff0c;并将原图和标注后的图像一同保存&#xff0c;以便查看 两个draw_labels函数&#xff0c;分…

【译文】使用ANSI码丰富命令行输出

每个人都习惯了在终端中打印输出的程序&#xff0c;当新文本出现时&#xff0c;它会滚动&#xff0c;但这并不是您所能做的全部:您的程序可以为文本上色&#xff0c;上下左右移动光标&#xff0c;或者在以后要重新打印时清除屏幕的部分内容。这就是为什么像Git这样的程序可以实…

远程过程调用-buttonrpc源码解析2-元组与可变参模板

在不考虑远程调用的情况下&#xff0c;假设我们调用的是本地某个函数&#xff0c;为了支持参数的数量和类型可变&#xff0c;需要使用可变参模板&#xff0c;常见形式如下&#xff1a; // 具体实现函数&#xff1a;利用C17提供的折叠表达式来解析参数包 template<typename …

JAVA实战手册-开篇总述

该专题以实战为出发点&#xff0c;总结概述了实际工作中常用的java知识点&#xff0c;掌握了这些知识点&#xff0c;日常工作开发以及面试都不在话下。 话不多说&#xff0c;直入正题&#xff0c;以下为JAVA知识点概括总结&#xff08;总计涵盖了10大类78小项&#xff09; 针对…

激光打标机:精准定位,实现个性化标识需求

激光打标机&#xff1a;精准定位&#xff0c;实现个性化标识需求 激光打标机&#xff0c;以其精准定位的特性&#xff0c;成为实现个性化标识需求的得力工具。在现代制造业中&#xff0c;个性化标识已成为产品差异化、品牌塑造和提升附加值的重要手段。激光打标机通过其独特的打…

前后端项目笔记

前端项目创建 准备工作 nodejs安装 vue cli安装 vue create frontend 最后一个y的话 它会保存 方便下次创建项目 我这是手快敲错了 随自己 前端项目组件及作用 Element-UI引入 安装 npm i element-ui -S main.js中引入 清空路口App.vue 清空Home页面 随便写个按钮 原因…

【docker】Docker打包SpringBoot镜像

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;中间件 ⛺️稳中求进&#xff0c;晒太阳 前置说明 最为原始的打包方式spring-boot-maven-plugin插件jib-maven-plugin插件dockerfle-maven-plugin插件 最为原始的方式 也就是使用Docker的打…

Ubuntu Desktop - Desktop

Ubuntu Desktop - Desktop 1. Amazon2. Ubuntu Software3. Desktop4. 系统桌面快捷方式5. 用户桌面快捷方式References 1. Amazon Amazon -> Unlock from Launcher 2. Ubuntu Software Installed -> Games -> Remove 3. Desktop /home/strong/Desktop 4. 系统桌面…

爆肝五千字!ECMAScript核心概念与现代JavaScript特性全解析

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

KEY ENERGY欧洲意大利能源光伏储能展

3月1号第18届意大利里米尼国际可再生能源展&#xff08;KEY ENERGY&#xff09;由知名主办方ITALIAN EXHIBITION GROUP S.P.A组织举办&#xff0c;每年一届&#xff0c;是欧洲第二大能源展&#xff0c;也是覆盖范围最全知名度最高的可再生能源展览会。 该展会将于2024扩大规模…

【Mysql数据库基础03】分组函数(聚合函数)、分组查询

分组函数(聚合函数&#xff09;、分组查询 1 分组函数1.1 简单的使用1.2 是否忽略null值1.3 和关键字搭配使用1.4 count函数的详细介绍1.5 练习 2 分组查询Group by2.1 简单的分组查询2.2 练习 3 格式投票:yum: 1 分组函数 1.1 简单的使用 COUNT(expression)&#xff1a;计算符…

腾讯云服务器租用价格多少钱一个月?2024优惠价格表

2024腾讯云服务器多少钱一个月&#xff1f;5元1个月起&#xff0c;腾讯云轻量服务器4核16G12M带宽32元1个月、96元3个月&#xff0c;8核32G22M配置115元一个月、345元3个月&#xff0c;腾讯云轻量应用服务器61元一年折合5元一个月、4核8G12M配置646元15个月、2核4G5M服务器165元…

腾讯云服务器多少钱1个月?2024一个月收费阿济格IE吧

2024腾讯云服务器多少钱一个月&#xff1f;5元1个月起&#xff0c;腾讯云轻量服务器4核16G12M带宽32元1个月、96元3个月&#xff0c;8核32G22M配置115元一个月、345元3个月&#xff0c;腾讯云轻量应用服务器61元一年折合5元一个月、4核8G12M配置646元15个月、2核4G5M服务器165元…

计算机考研|双非一战135上岸,408经验分享+复盘

计算机专业的同学真的别想的太天真&#xff01; 相比于其他专业&#xff0c;计算机专业的同学其实还是很有优势的 但是现在随着计算机专业的同学越来越多&#xff0c;找工作的困难程度以及学历自然而然被卷起来了 以前的算法岗基本要求在本科以上&#xff0c;现在基本都是非92研…

MacBook远程控制工具VNC Viewer_亲测使用

MacBook远程控制工具VNC Viewer_亲测使用 官方下载地址: https://www.realvnc.com/en/connect/download/viewer/ MacBook远程桌面Windows使用Microsoft Remote Desktop for Mac_亲测使用 VNC 介绍 VNC (Virtual Network Console)是虚拟网络控制台的缩写。它是一款优秀的远程…