【linux】进程间通信——system V

news2024/9/29 13:15:14

system V

  • 一、system V介绍
  • 二 、共享内存
    • 2.1 共享内存的原理
    • 2.2 共享内存接口
      • 2.2.1 创建共享内存shmget
      • 2.2.2 查看IPC资源
      • 2.2.3 共享内存的控制shmctl
      • 2.2.4 共享内存的关联shmat
      • 2.2.5 共享内存的去关联shmdt
    • 2.3 进程间通信
    • 2.4 共享内存的特性
    • 2.5 共享内存的大小
  • 三、消息队列
    • 3.1 消息队列的概念
    • 3.2 消息队列接口
      • 3.2.1 消息队列的获取msgget
      • 3.2.2 消息队列的控制msgctl
      • 3.2.3 消息队列发送数据msgsnd
      • 3.2.4 消息队列获取msgrcv
  • 四、信号量
    • 4.1 信号量概念
    • 4.2 信号量的用处
    • 4.3 信号量的pv操作
    • 4.4 信号量接口
      • 4.4.1 信号量申请semget
      • 4.4.2 信号量控制semctl
      • 4.4.3 信号量操作semop
  • 五、总结

一、system V介绍

进程间通信除了通过管道,都是基于文件的通信方式,还有一种方式是:SystemV标准的进程间通信方式。SystemV是一个在OS层面专门为进程通信设计的一个方案。这些都是由计算机科学家和程序员设计的,并且需要给用户使用。

如果要给用户用,是以什么方式给用户使用的呢?在操作系统层面上,SystemV是OS内核的一部分,是为OS中多进程提供的一种通信方案。但是OS不相信任何用户,给用户提供功能的时候,采用系统调用。所以System V进程间通信,一定会存在专门用来通信的接口:system call。

因为在早期由很多的方案,但是我们需要统一使用一个方案,所以现在诞生了在统一主机内的进程间通信方案:system V方案

system V IPC提供的通信方式有三种: 共享内存、消息队列、信号量

二 、共享内存

2.1 共享内存的原理

前面说过两个进程要通信就需要看到同一块资源

而我们知道进程之间具有独立性,物理内存当中代码和数据也互相独立。
那么现在我们可以在物理内存中创建一个内存块,让不同的进程都能看到这个内存块,具体的做法如下:

1️⃣ 通过某种调用,在内存中创建一份内存空间。
2️⃣ 通过某种调用,让进程”挂接“到这份内存空间上。(将创建好的内存映射进进程地址空间)
在后面可能不会共享内存了。所以在不用共享内存的时候
3️⃣ 去关联(去挂接)。
4️⃣ 释放共享内存。

对于共享内存的理解:
OS内可能存在多个进程同时使用不同的共享内存来进行进程间通信,既然有多份共享内存,那么操作系统就要管理它们,按照前面学习的经验,先描述后组织,描述就是对共享内存的一系列属性进行描述,而后用数据结构组织起来,这样对共享内存的管理变成了对数据结构的操作。

2.2 共享内存接口

2.2.1 创建共享内存shmget

shmget:用来创建共享内存

 #include <sys/ipc.h>
 #include <sys/shm.h>

 int shmget(key_t key, size_t size, int shmflg);
 RETURN VALUE
 On success, a valid shared memory identifier is returned.  
 On errir, -1 is returned, and errno is set to indicate the error.

参数说明:

size:共享内存的大小。
shmflag:通常有两种选项:IPC_CREAT、IPC_EXCL。
IPC_CREAT: 共享内存如果不存在,则创建,不存在则获取。
IPC_EXCL: 无法单独使用
IPC_CREAT | IPC_EXCL: 如果不存在就创建,如果存在就出错返回(保证共享内存是新创建的)。
如果shmflag是0默认就是IPC_CREAT。
key:保证看到同一份共享内存,能进行唯一性标识(就像省份证号码一样,数字不重要,只用来标识唯一性)。通过ftok函数转化
如果创建成功返回共享内存的标识符,如果失败则返回-1。

ftok:用来形成key

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
RETURN VALUE
On success, the generated key_t value is returned.  
On failure -1 is returned, with errno indicating the error as for the stat(2) system call.

ftok第一个参数是自定义路径名,第二个参数是自定义的项目ID。最后唯一的共享内存ID就是通过路径名+项目ID来标识。最后生成的返回值并不重要,只要它能生成一个值来唯一标识这块共享内存就可以。
只要参数不变,生成的返回值就不会变。 这样就可以让两个进程拥有同一个key,就可以用key找到同一份共享内存。

key_t GetKey()
{
    key_t n = ftok(PATHNAME, PROJ_ID);
    if(n == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return n;
}

int Creatshm(key_t key)
{
    int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if(shmid == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return shmid;
}

int Getshm(key_t key)
{
    int shmid = shmget(key, SIZE, IPC_CREAT | 0666);
    if(shmid == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return shmid;
}

key的理解:
上面也说过了OS会把内存块管理起来,共享内存=物理内存块+共享内存的相关属性描述共享内存时就有一个字段struct shm中有key。 一个进程创建内存块后把key值写进相关属性中,而另一个进程拿着key值遍历相关属性查找。这样就完成了两个进程共享一块内存块。
我们发现key和shmid都是标识内存块的:key是在内核标识唯一性,而shmid是在用户层标识唯一性,这样即使操作系统有什么变化也不会影响用户使用,充分的解耦。他们的关系就类似于fd与inode。

2.2.2 查看IPC资源

共享内存的生命周期不是随着进程的,而是随着OS的,这也是所有system V进程间通信的特征。

ipcs -m 查看共享内存
在这里插入图片描述
ipcs -q 查看消息队列
ipcs -s 查看信号量
在这里插入图片描述

ipcs 三个一起查看
在这里插入图片描述
ipcrm -m shmid 删除共享内存
在这里插入图片描述

2.2.3 共享内存的控制shmctl

shmctl:控制共享内存

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

它的作用是对共享内存的控制
参数介绍:
shmid:控制共享内存的标识符。
cmd:控制的种类,主要用的是IPC_RMID(立即移除共享内存)。
buf:控制共享内存的数据结构,设置为空即可。
返回值:0表示返回成功,-1表示失败。

void Delshm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
}

2.2.4 共享内存的关联shmat

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数介绍:
shmaddr:可以指定虚拟内存,设置为nullptr即可。
shmflg:读取权限,默认为0。
返回值:返回共享内存的起始地址

void* Attachshm(int shmid)
{
    void* mem = shmat(shmid, nullptr, 0);
    if((long long)mem == -1L)// 64位指针8字节
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return mem;
}

2.2.5 共享内存的去关联shmdt

去关联是指把进程和共享内存之间的映射关系删掉(修改页表),并不是删除共享内存。

#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);

它的参数就是在shmat返回的参数。
成功返回0,失败返回-1。

void Detachshm(void* start)
{
    if(shmdt(start) == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
}

2.3 进程间通信

comm.hpp

#ifndef _COMM_HPP_
#define _COMM_HPP_

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>

#define PATHNAME ".."
#define PROJ_ID 12345
#define SIZE 4096

key_t GetKey()
{
    key_t n = ftok(PATHNAME, PROJ_ID);
    if(n == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return n;
}

int Creatshm(key_t key)
{
    int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if(shmid == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return shmid;
}

int Getshm(key_t key)
{
    int shmid = shmget(key, SIZE, IPC_CREAT | 0666);
    if(shmid == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return shmid;
}

void Delshm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
}

void* Attachshm(int shmid)
{
    void* mem = shmat(shmid, nullptr, 0);
    if((long long)mem == -1L)// 64位指针8字节
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
    return mem;
}

void Detachshm(void* start)
{
    if(shmdt(start) == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        assert(false);
    }
}

#endif

shm_a.cc

#include "comm.hpp"

int main()
{
    key_t k = GetKey();
    // 创建共享内存
    int shmid = Creatshm(k);
    // 挂接
    char* start = (char*)Attachshm(shmid);
    //使用
    while(true)
    {
        if(strlen(start) > 4)
        {
            printf("%s\n", start);
        }
        if(strlen(start) == 4)
        {
            break;
        }
        sleep(1);
    }
    // 去关联
    Detachshm(start);
    // 删除共享内存
    Delshm(shmid);
    return 0;
}

shm_b.cc

#include "comm.hpp"

int main()
{
    key_t k = GetKey();
    // 获取共享内存
    int shmid = Getshm(k);
    // 挂接
    char* start = (char*)Attachshm(shmid);
    // 使用
    std::string str = "hello shm";
    int cnt = 0;
    while(cnt != 8)
    {
        snprintf(start, SIZE, "%s[%d]", str.c_str(),  cnt);
        cnt++;
        sleep(1);
    }
    std::string cmd = "stop";
    snprintf(start, SIZE, "%s", cmd.c_str());
    // 去关联
    Detachshm(start);
    return 0;
}

2.4 共享内存的特性

1️⃣ 共享内存是所有的进程间通信速度最快的。(优点)
2️⃣ 共享内存不提供任何同步或者互斥机制,不提供不代表不需要,所以需要程序员自行保证数据的安全!这也造成了共享内存在多进程中是不太安全的。(缺点)
3️⃣ 共享内存的生命周期是随OS的,而不是随进程的,这是所有System V进程间通信的共性。

为什么速度快呢?
管道和共享内存:考虑键盘输入,和显示器输出,对于同一份数据,共享内存有几次数据拷贝,管道有几次数据拷贝

管道:在这里插入图片描述
可以看到写到管道需要拷贝两次,而另一个进程读也需要两次,所以一共四次。如果考虑到输入输出外设,那么就是六次。

共享内存:
直接写入共享内存,直接从共享内存输出,所以是两次。考虑到输入输出的话就是四次。

2.5 共享内存的大小

这里讲的是shget的第二个参数,一般建议设置成4096(4KB)的整数倍,因为系统分享内存是以4KB为单位,假如我们申请的是4097,那么系统就会直接向上取整,也就是4096*2,但是我们只能使用其中的4097大小。

三、消息队列

3.1 消息队列的概念

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法,读端和写端公用一个队列,每个数据块就是队列的一个节点,每个数据块都会有个记录类型的数据,来判断该数据块该被哪个进程读取。

3.2 消息队列接口

3.2.1 消息队列的获取msgget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

这里的msgflg跟共享内存的两个参数一摸一样(IPC_CREAT、IPC_EXCL)。

返回值:msgget函数返回的一个有效的消息队列标识符

3.2.2 消息队列的控制msgctl

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数说明:
msqid:消息队列的标识符。
其他的参数跟shmctl一样。

3.2.3 消息队列发送数据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即可
成功返回0,失败返回-1

3.2.4 消息队列获取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)

参数跟上面msgsnd一致。

四、信号量

4.1 信号量概念

信号量本质上就是个计数器,它统计的是公共资源资源还剩多少

公共资源:可以被多个进程同时访问的资源,而如果访问没有被保护的公共资源,就会导致数据不一致问题(一个进程还在写的时候另一个进程就开始读)。所以公共资源需要保护,被保护起来的资源称为临界资源。而访问这些临界资源的那部分代码称为临界区。其他的代码就称为非临界区

保护公共资源:同步和互斥

互斥:当有多个进程想要访问同一份资源的时候,我们只允许一个进程访问,当这个进程访问完了,下一个进程才能访问。

原子性:要么不做,要么做完,只有这两种状态的情况。

既然我们想让多个进程看到同一个计数器,那么信号量也是个公共资源

4.2 信号量的用处

举个例子,假设我们要去看电影,而里面的座位只有我们买了票才能拥有,这里的票就是信号量,我们买了一张后,信号量就--

所以当我们想要某种资源的时候,我们可以进行预定(买票成功)。
共享资源可以作为一个整体使用或者划分成多个子资源部分。大部分都是整体使用,比如果管道。

如果我们申请成功,相当于我们预定了共享内存中的一小部分资源。如果不成功,就不能访问共享资源,以达到保护其他进程的目的。

在访问公共资源前要先申请信号量,而信号量本身就是个公共资源,那么信号量也带保护自己的安全。那么如何保证呢?

4.3 信号量的pv操作

信号量操作本质上就是计数器的++或者--,是原子性的。当我们预定资源的时候(--)称为p操作,释放资源(++)称为v操作。
如果信号量的初始值是1就代表了访问公共资源作为一个整体来使用,一个进程申请了别的进程就不能再申请了,我们把只有两种状态的信号量称为二元信号量

4.4 信号量接口

4.4.1 信号量申请semget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

参数介绍:

nsems:表示申请信号量的个数
第一个和第三个参数就跟前面的shmget相同。
返回值:信号量集创建成功时,semget函数返回的一个有效的信号量集标识符

4.4.2 信号量控制semctl

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

参数介绍:
semid:信号量标识符
semnum:信号量的下标,默认0

4.4.3 信号量操作semop

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

参数介绍:
sembuf:一个结构体

在这里插入图片描述
sem_num:信号量下标。
sem_op:1表示++p操作,-1表示--v操作。
sem_flg:选项设为0即可。

nsops:有多少个sembuf结构体。

五、总结

我们可以发现,共享内存、消息队列、信号量接口相似度非常高,尤其是获取与删除,这些都是system V标准的进程间通信
他们的第一个成员都是ipc_perm

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 */
       };

我们可能会申请共享内存、消息队列、信号量的任意一种,那么操作系统如何组织它们呢?

因为ipc_perm是一样的,所以可以维护一个struct ipc_perm*的指针数组,存共享内存、消息队列、信号量它们的第一个元素的地址,也就是&ipc_perm
而我们知道结构体的第一个成员的地址和结构体对象的地址在数字上是相等的

所以当我们想使用的时候直接强转成想用的结构体(共享内存、消息队列、信号量)就可以。

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

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

相关文章

【Virtualization】Windows11安装VMware Workstation后异常处置

安装环境 Windows 11 专业版 22H2 build 22621.1265 VMware Workstation 17 Pro 17.0.0 build-20800274 存在问题 原因分析 1、BIOS未开启虚拟化。 2、操作系统启用的虚拟化与Workstation冲突。 3、操作系统启用内核隔离-内存完整性保护。 处置思路 1、打开“资源管理器”…

MyBatis学习笔记(六) —— MyBatis的各种查询功能

6、MyBatis的各种查询功能 6.1、查询一个实体类对象 SelectMapper.java接口 /*** 根据用户id查询用户信息* param id* return*/ User getUserById(Param("id") int id);SelectMapper.xml <!--User getUserById(Param("id") int id)--> <selec…

第十三届蓝桥杯国赛 C++ B 组 J 题——搬砖(AC)

目录1.搬砖1.题目描述2.输入格式3.输出格式4.样例输入5.样例输出6.数据范围7.原题链接2.解题思路3.Ac_code1.搬砖 1.题目描述 这天&#xff0c;小明在搬砖。 他一共有 nnn 块砖, 他发现第 iii 砖的重量为 wiw_{i}wi​, 价值为 viv_{i}vi​ 。他突然想从这些 砖中选一些出来从…

JVM的内存结构

一.引言 1.什么是 JVM &#xff1f; 1&#xff09;定义&#xff1a; Java Virtual Machine - java 程序的运行环境&#xff08;java 二进制字节码的运行环境&#xff09; 2&#xff09;好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收功能…

在服务器上指定GPU跑代码

一、准备工作 查看GPU状态和信息&#xff0c;找到空闲的GPU&#xff1a; nvidia-smi 二、指定单GPU 从图中 Processes 表格我们可以发现0、1、2号GPU均是可以利用的。于是我们可以在python文件中加入以下代码&#xff0c;表示使用0号GPU&#xff1a; import os os.environ…

【数通网络交换基础梳理2】三层设备、网关、ARP表、VLAN、路由表及跨网段路由下一跳转发原理

一、不同网段如何通讯 同网段可以依靠二层交换机通讯&#xff0c;网络中存在多个网段192.168.1.1/24 172.16.1.1/24 173.73.1.1/24情况下如何互相通讯&#xff1f;上节留一下的问题&#xff0c;这节继续讲解。 1、这里以Ping命令讲解&#xff0c;PC1 ping173.73.1.2&#xf…

2023中职组网络安全技能竞赛——代码审计解析(超级详细)

代码审计:需求环境可私信博主 任务环境说明: 服务器场景:PYsystem0035服务器场景操作系统:未知服务器场景用户名:未知 密码:未知在渗透机Kali Linux中访问靶机服务器Web页面,注册账号并登陆,找到存在XSS执行漏洞的页面,将该页面中存在XSS执行漏洞的对象名作为FLAG提…

关于永中Office(永中办公软件)不认Windows系统安装的字体的解决办法

一位网友的电脑最近安装了永中Office软件&#xff0c;在使用过程中发现无法使用方正小标宋简体、仿宋GB2312等字体&#xff0c;这些字体在之前所用的微软Office中可以正常使用。他根据网上查到的一些的资料&#xff0c;将这些字体文件复制到C:\Program Files\Yozosoft\Yozo_Off…

【Java】CAS锁

一、什么是CAS机制&#xff08;compare and swap&#xff09; 1.概述 CAS的全称为Compare-And-Swap&#xff0c;直译就是对比交换。是一条CPU的原子指令&#xff0c;其作用是让CPU先进行比较两个值是否相等&#xff0c;然后原子地更新某个位置的值。经过调查发现&#xff0c;…

测试——基本概念

概念 测试和调试有以下几点区别&#xff1a; 测试是测试人员进行的工作&#xff0c;调试是开发人员调试是发现并解决问题&#xff0c;测试只是发现问题测试贯穿于整个项目的生命周期&#xff0c;而调试主要在编码阶段 测试人员一般有如下的工作&#xff1a; 需求分析&#x…

Spring MVC 源码- ViewResolver 组件

ViewResolver 组件ViewResolver 组件&#xff0c;视图解析器&#xff0c;根据视图名和国际化&#xff0c;获得最终的视图 View 对象回顾先来回顾一下在 DispatcherServlet 中处理请求的过程中哪里使用到 ViewResolver 组件&#xff0c;可以回到《一个请求响应的旅行过程》中的 …

进程地址空间(虚拟地址空间)

目录 引入问题 测试代码 引入地址空间 故事1&#xff1a; 故事二&#xff1a; 解决问题 为什么有虚拟地址空间 扩展 扩展1&#xff08;没有地址空间&#xff0c;OS如何工作&#xff09; 扩展2 &#xff08;代码只读深入了解&#xff09; 扩展3&#xff08;malloc本质…

0 初识Kotlin

0 基本介绍 相信很多开发者对Kotlin还是比较陌生的。 Kotlin是一种新型的编程语言&#xff0c;由JetBrains公司开发与设计&#xff0c;在2012年开源&#xff0c; 但没引起什么注意。 直到2017年google宣布将Kotlin作为Android开发的首选语言&#xff0c;Kotlin才开始大放异彩。…

基于MATLAB的MIMO预编码设计:优化迫零算法(附完整代码与分析)

目录 一.介绍 二. 对比本方案优化后的迫零算法与原始的迫零算法 三. 源代码 四. 运行结果及分析 4.1 天线数为8 4.2 天线数为128 一.介绍 图中“RF Chain” 全称为Radio Frequency Chain&#xff0c;代表射频链路。 此MIMO预编码包含了基带预编码W&#xff08;改变幅度和…

NVIDIA GPU开源驱动编译学习架构分析

2022年5月&#xff0c;社区终于等到了这一天&#xff0c;NVIDIA开源了他们的LINUX GPU 内核驱动&#xff0c; Linux 内核总设计师 Linus Torvalds 十年前说过的一句话&#xff0c;大概意思是英伟达是LINUX开发者遇到的硬件厂商中最麻烦的一个&#xff0c;说完这句话之后&#x…

20230225英语学习

Is Your Phone Heavier When It’s Full of Data? We’ve Done the Math 从数学角度看&#xff0c;充满数据的手机会更重吗&#xff1f; Here’s a weird question: does your phone weigh more when it’s “full” than when it’s “empty”?It sounds almost ridiculou…

【unity游戏制作-mango的冒险】场景二的镜头和法球特效跟随

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity游戏制作 ⭐mango的冒险场景二——镜头和法球特效跟随⭐ 文章目录⭐mango的冒险场景二——镜…

C#的多线程、线程池和Task

线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作&#xff0c;那么设置不同的线程执行路径往往是有益的&#xff0c;每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的…

如何使用Python和ftplib模块连接到FTP服务器并列出远程目录中的文件?

ftp服务可以用在以下一些使用场景&#xff1a; 文件共享&#xff1a;使用Python和FTP服务器可以轻松地搭建一个文件共享服务&#xff0c;使得用户可以上传和下载文件&#xff0c;从而促进协作和信息共享。 数据备份&#xff1a;FTP可以用于将数据备份到另一个服务器或云存储中…

Git ---- GitHub 操作

Git ---- GitHub 操作1. 创建远程仓库2. 远程仓库操作1. 创建爱你远程仓库别名2. 推送本地分支到远程仓库3. 克隆远程仓库到本地4. 邀请加入团队5. 拉取远程库内容3. 跨团队协作4. SSH 免密登录GitHub 网址&#xff1a;https://github.com/ Ps&#xff1a;全球最大同性交友网站…