iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >详解python网络进程
  • 137
分享到

详解python网络进程

python网络进程 2022-06-02 22:06:29 137人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录一、多任务编程二、进程三、os.fork创建进程3.1、进程ID和退出函数四、孤儿和僵尸4.1、孤儿进程4.2、僵尸进程4.3、如何避免僵尸进程的产生五、Multiprocessing创建进程5.1、multip

目录
  • 一、多任务编程
  • 二、进程
  • 三、os.fork创建进程
    • 3.1、进程ID和退出函数
  • 四、孤儿和僵尸
    • 4.1、孤儿进程
    • 4.2、僵尸进程
    • 4.3、如何避免僵尸进程的产生
  • 五、Multiprocessing创建进程
    • 5.1、multiprocessing进程属性
  • 六、进程池
    • 七、进程间通信(IPC)
      • 7.1、管道通信(Pipe)
      • 7.2、消息队列
      • 7.3、共享内存
      • 7.4、信号量(信号灯集)

    一、多任务编程

    意义:充分利用计算机的资源提高程序的运行效率

    定义:通过应用程序利用计算机多个核心,达到同时执行多个任务的目的

    实施方案: 多进程、多线程

    并行:多个计算机核心并行的同时处理多个任务

    并发:内核在多个任务间不断切换,达到好像内核在同时处理多个任务的运行效果

    进程:程序在计算机中运行一次的过程

    程序:是一个可执行文件,是静态的,占有磁盘,不占有计算机运行资源

    进程:进程是一个动态的过程描述,占有CPU内存等计算机资源的,有一定的生命周期

    同一个程序的不同执行过程是不同的进程,因为分配的计算机资源等均不同

    父子进程 : 系统中每一个进程(除了系统初始化进程)都有唯一的父进程,可以有0个或多个子进
    程。父子进程关系便于进程管理。

    二、进程

    CPU时间片:如果一个进程在某个时间点被计算机分配了内核,我们称为该进程在CPU时间片上。

    PCB(进程控制块):存放进程消息的空间

    进程ID(PID):进程在操作系统中的唯一编号,由系统自动分配

    进程信息包括:进程PID,进程占有的内存位置,创建时间,创建用户. . . . . . . .

    进程特征:

    • 进程是操作系统分配计算机资源的最小单位
    • 每一个进程都有自己单独的虚拟内存空间
    • 进程间的执行相互独立,互不影响

    进程的状态

    三态

    • 就绪态:进程具备执行条件,等待系统分配CPU
    • 运行态:进程占有CPU处理器,处于运行状态
    • 等待态:进程暂时不具备运行条件,需要阻塞等待,让出CPU

    五态(增加新建态和终止态)

    • 新建态:创建一个新的进程,获取资源的过程
    • 终止态:进程结束释放资源的过程

    查看进程树:pstree

    查看父进程PID:ps -ajx

    linux查看进程命令:ps -aux

    有一列为STAT为进程的状态

    • D 等待态 (不可中断等待)(阻塞)
    • S 等待态 (可中断等待)(睡眠)
    • T 等待态 (暂停状态)
    • R 运行态 (就绪态运行态)
    • Z 僵尸态
    • + 前台进程(能在终端显示出现象的)
    • < 高优先级
    • N 低优先级
    • l 有多线程
    • s 会话组组长

    三、os.fork创建进程

    pid = os.fork() 

    功能:创建一个子进程

    返回值:创建成功在原有的进程中返回子进程的PID,在子进程中返回0;创建失败返回一个负数

    父子进程通常会根据fork返回值的差异选择执行不同的代码(使用if结构)

    
    import  os 
    from time import sleep
    
    pid = os.fork()
    
    if pid < 0:
        print("创建进程失败")
    
    #子进程执行部分
    elif pid == 0:
        print("新进程创建成功")
    
    #父进程执行部分
    else:
        sleep(1)
        print("原来的进程")
    
    print("程序执行完毕")
    
    # 新进程创建成功
    # 原来的进程
    # 程序执行完毕
    • 子进程会复制父进程全部代码段(包括fork前的代码)但是子进程仅从fork的下一句开始执行
    • 父进程不一定先执行(进程之间相互独立,互不影响)
    • 父子进程各有自己的属性特征,比如:PID号PCB内存空间
    • 父进程fork之前开辟的空间子进程同样拥有,但是进程之间相互独立,互不影响.

    父子进程的变量域

    
    import os 
    from time import sleep 
    
    a = 1
    pid = os.fork()
    if pid < 0:
        print("创建进程失败")
    elif pid == 0:
        print("子进程")
        print("a = ",a)
        a = 10000
        print("a = ",a)
    else:
        sleep(1)
        print("父进程")
        print("parent a :",a)    # a = 1
    
    # 子进程
    # a =  1
    # a =  10000
    # 父进程
    # parent a : 1

    3.1、进程ID和退出函数

    os.getpid()获取当前进程的PID号

    返回值:返回PID号

    os.getppid()获取父类进程的进程号

    返回值:返回PID号

    
    import os
    
    pid = os.fork()
    
    if pid < 0:
      print("Error")
    elif pid == 0:
      print("Child PID:", os.getpid())       # 26537
      print("Get parent PID:", os.getppid()) # 26536
    else:
      print("Get child PID:", pid)           # 26537
      print("Parent PID:", os.getpid())      # 26536

    os._exit(status)退出进程

    参数:进程的退出状态  整数

    sys.exit([status])退出进程

    参数:默认为0 整数则表示退出状态;符串则表示退出时打印内容

    sys.exit([status])可以通过捕获SystemExit异常阻止退出

    
    import os,sys
    
    # os._exit(0)                 # 退出进程
    try:
        sys.exit("退出")
    except SystemExit as e:
        print("退出原因:",e)    # 退出原因: 退出

    四、孤儿和僵尸

    4.1、孤儿进程

    父进程先于子进程退出,此时子进程就会变成孤儿进程

    孤儿进程会被系统指定的进程收养,即系统进程会成为该孤儿进程新的父进程。孤儿进程退出时该父进程会处理退出状态

    4.2、僵尸进程

    子进程先与父进程退出,父进程没有处理子进程退出状态,此时子进程成为僵尸进程

    僵尸进程已经结束,但是会滞留部分PCB信息在内存,大量的僵尸会消耗系统资源,应该尽量避免

    4.3、如何避免僵尸进程的产生

    父进程处理子进程退出状态

    pid, status = os.wait()

    功能:在父进程中阻塞等待处理子进程的退出

    返回值:pid 退出的子进程的PID号

    status 子进程的退出状态

    
    import os, sys
    
    pid = os.fork()
    
    if pid < 0:
      print("Error")
    elif pid == 0:
      print("Child process", os.getpid())   # Child process 27248
      sys.exit(1)
    else:
      pid, status = os.wait()     # 阻塞等待子进程退出
      print("pid : ", pid)        # pid :  27248
      # 还原退出状态
      print("status:", os.WEXITSTATUS(status))      # status: 1
      while True:
        pass

    创建二级子进程

    • 父进程创建子进程等待子进程退出
    • 子进程创建二级子进程,然后马上退出
    • 二级子进程成为孤儿,处理具体事件
    
    import os
    from time import sleep
    
    def fun1():
        sleep(3)
        print("第一件事情")
    
    def fun2():
        sleep(4)
        print("第二件事情")
    
    pid = os.fork()
    
    if pid < 0:
        print("Create process error")
    elif pid == 0:          # 子进程
        pid0 = os.fork()    # 创建二级进程
        if pid0 < 0:
            print("创建二级进程失败")
        elif pid0 == 0:     # 二级子进程
            fun2()          # 做第二件事
        else:               # 二级进程
            os._exit(0)     # 二级进程退出
    else:
        os.wait()
        fun1()              # 做第一件事
    
    # 第一件事情
    # 第二件事情

    通过信号处理子进程退出

    原理: 子进程退出时会发送信号给父进程,如果父进程忽略子进程信号, 则系统就会自动处理子进程退出。

    方法: 使用signal模块在父进程创建子进程前写如下语句 :

    import signal

    signal.signal(signal.SIGCHLD,signal.SIG_IGN)

    特点 : 非阻塞,不会影响父进程运行。可以处理所有子进程退出

    五、Multiprocessing创建进程

    步骤:

    • 需要将要做的事情封装成函数
    • multiprocessing.Process创建进程,并绑定函数
    • start启动进程
    • join回收进程

    p = multiprocessing.Process(target, [name], [args], [kwargs])

    创建进程对象

    参数:

    • target : 要绑定的函数名
    • name : 给进程起的名称 (默认Process-1)
    • args:元组用来给target函数传参
    • kwargs :字典用来给target函数键值传参

    p.start()

    功能: 启动进程 自动运行terget绑定函数。此时进程被创建

    p.join([timeout])

    功能: 阻塞等待子进程退出,最后回收进程

    参数: 超时时间

    multiprocessing的注意事项:

    • 使用multiprocessing创建进程子进程同样复制父进程的全部内存空间,之后有自己独立的空间,执行上互不干扰
    • 如果不使用join回收可能会产生僵尸进程
    • 一般父进程功能就是创建子进程回收子进程,所有事件交给子进程完成
    • multiprocessing创建的子进程无法使用ptint
    
    import multiprocessing as mp 
    from time import sleep 
    import os
    
    a = 1
    
    def fun():
        sleep(2)
        print("子进程事件",os.getpid())
        global a
        a = 10000
        print("a = ",a)
    
    p = mp.Process(target = fun)    # 创建进程对象
    p.start()   # 启动进程
    sleep(3)
    print("这是父进程")
    p.join()    # 回收进程
    print("parent a:",a)
    
    # 子进程事件 5434
    # a =  10000
    # 这是父进程
    # parent a: 1
    
    Process(target)

    5.1、multiprocessing进程属性

    p.name :进程名称

    p.pid :对应子进程的PID号

    p.is_alive():查看子进程是否在生命周期

    p.daemon: 设置父子进程的退出关系

    如果等于True则子进程会随父进程的退出而结束,就不用使用 join(),必须要求在start()前设置

    六、进程池

    引言:如果有大量的任务需要多进程完成,而任务周期又比较短且需要频繁创建。此时可能产生大量进程频繁创建销毁的情况,消耗计算机资源较大,这个时候就需要进程池技术

    进程池的原理:创建一定数量的进程来处理事件,事件处理完进程不退出而是继续处理其他事件,直到所有事件全都处理完毕统一销毁。增加进程的重复利用,降低资源消耗。

    1.创建进程池,在池内放入适当数量的进程

    from multiprocessing import Pool

    Pool(processes)  创建进程池对象

    • 参数:进程数量
    • 返回 :指定进程数量,默认根据系统自动判定

    2.将事件封装函数,放入到进程池

    pool.apply_async(fun,args,kwds)  将事件放入进程池执行

    参数:

    • fun 要执行的事件函数
    • args 以元组为fun传参
    • kwds 以字典为fun传参

    返回值 :返回一个事件对象 通过get()属性函数可以获取fun的返回值

    3.关闭进程池

    pool.close():关闭进程池,无法再加入事件

    4.回收进程

    pool.join():回收进程池

    
    from multiprocessing import Pool
    from time import sleep,ctime
    
    pool = Pool(4)    # 创建进程池
    # 进程池事件
    def worker(msg):
      sleep(2)
      print(msg)
      return ctime()
    
    # 向进程池添加执行事件
    for i in range(4):
      msg = "Hello %d"%i
    
      # r 代表func事件的一个对象
      r = pool.apply_async(func=worker,args=(msg,))
    
    pool.close()    # 关闭进程池
    pool.join()     # 回收进程池
    
    # Hello 3
    # Hello 2
    # Hello 0
    # Hello 1

    七、进程间通信(IPC)

    由于进程间空间独立,资源无法共享,此时在进程间通信就需要专门的通信方法。

    进程间通信方法 : 管道 消息队列 共享内存 信号信号量 套接字

    7.1、管道通信(Pipe)

    通信原理:在内存中开辟管道空间,生成管道操作对象,多个进程使用同一个管道对象进行读写即可实现通信 

    from multiprocessing import Pipe

    fd1, fd2 = Pipe(duplex = True)

    • 功能:创建管道
    • 参数:默认表示双向管道,如果为False 表示单向管道
    • 返回值:表示管道两端的读写对象;如果是双向管道均可读写;如果是单向管道fd1只读 fd2只写

    fd.recv()

    • 功能 : 从管道获取内容
    • 返回值:获取到的数据,当管道为空则阻塞

    fd.send(data)

    • 功能: 向管道写入内容
    • 参数: 要写入的数据

    注意:

    • multiprocessing中管道通信只能用于父子关系进程中
    • 管道对象在父进程中创建,子进程通过父进程获取
    
    from multiprocessing import Pipe, Process
    
    fd1, fd2 = Pipe()   # 创建管道,默认双向管道
    def fun1():
      data = fd1.recv()     # 从管道获取消息
      print("管道2传给管道1的数据", data)
      inpu = "跟你说句悄悄话"
      fd1.send(inpu)
    
    def fun2():
      fd2.send("肥水不流外人天")
      data = fd2.recv()
      print("管道1传给管道2的数据", data)
    
    p1 = Process(target=fun1)
    P2 = Process(target=fun2)
    
    p1.start()
    P2.start()
    
    p1.join()
    P2.join()
    # 管道2传给管道1的数据 肥水不流外人天
    # 管道1传给管道2的数据 跟你说句悄悄话

    7.2、消息队列

    从内存中开辟队列结构空间,多个进程可以向队列投放消息,在取出来的时候按照先进先出顺序取出

    q = Queue(maxsize = 0)  

    创建队列对象

    • maxsize :默认表示系统自动分配队列空间;如果传入正整数则表示最多存放多少条消息
    • 返回值 : 队列对象

    q.put(data,[block,timeout])  

    向队列中存入消息

    • data:存放消息(python数据类型)
    • block:默认为True表示当前队列满的时候阻塞,设置为False则表示非阻塞
    • timeout:当block为True表示超时时间

    返回值:返回获取的消息

    q.get([block,timeout])

    从队列取出消息

    • 参数:block 设置是否阻塞 False为非阻塞;timeout 超时检测
    • 返回值: 返回获取到的内容

    q.full():判断队列是否为满

    q.empty():判断队列是否为空

    q.qsize():判断当前队列有多少消息

    q.close():关闭队列

    
    from multiprocessing import Process, Queue
    from time import sleep
    from random import randint
    
    #  创建消息队列
    q = Queue(3)
    
    
    # 请求进程
    def request():
      for i in range(2):
        x = randint(0, 100)
        y = randint(0, 100)
        q.put((x, y))
    
    
    # 处理进程
    def handle():
      while True:
        sleep(1)
        try:
          x, y = q.get(timeout=2)
        except:
          break
        else:
          print("%d + %d = %d" % (x, y, x + y))
    
    
    p1 = Process(target=request)
    p2 = Process(target=handle)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    # 12 + 61 = 73
    # 69 + 48 = 117

    7.3、共享内存

    在内存中开辟一段空间,存储数据,对多个进程可见,每次写入共享内存中的数据会覆盖之前的内容,效率高,速度快

    from multiprocessing import Value, Array

    obj = Value(ctype,obj)

    功能:开辟共享内存空间

    参数:

    ctype:字符串,要转变的c的数据类型,对比类型对照表

    obj:共享内存的初始化数据

    返回:共享内存对象

    
    from multiprocessing import Process,Value
    import time
    from random import randint
    
    # 创建共享内存
    money = Value('i', 5000)
    
    #  修改共享内存
    def man():
      for i in range(30):
        time.sleep(0.2)
        money.value += randint(1, 1000)
    
    def girl():
      for i in range(30):
        time.sleep(0.15)
        money.value -= randint(100, 800)
    
    m = Process(target=man)
    g = Process(target=girl)
    m.start()
    g.start()
    m.join()
    g.join()
    
    print("一月余额:", money.value)   # 获取共享内存值
    # 一月余额: 4264

    obj = Array(ctype,obj)

    功能:开辟共享内存

    参数:

    ctype:要转化的c的类型

    obj:要存入共享的数据

    如果是列表将列表存入共享内存,要求数据类型一致

    如果是正整数表示开辟几个数据空间

    
    from multiprocessing import Process, Array
    
    # 创建共享内存
    # shm = Array('i',[1,2,3])
    # shm = Array('i',3)  # 表示开辟三个空间的列表
    shm = Array('c',b"hello") #字节串
    
    def fun():
      # 共享内存对象可迭代
      for i in shm:
        print(i)
      shm[0] = b'H'
    
    p = Process(target=fun)
    p.start()
    p.join()
    
    for i in shm:   # 子进程修改,父进程中也跟着修改
      print(i)
    
    print(shm.value) # 打印字节串 b'Hello'

    7.4、信号量(信号灯集)

    通信原理:给定一个数量对多个进程可见。多个进程都可以操作该数量增减,并根据数量值决定自己的行为。

    from multiprocessing import Semaphore
    sem = Semaphore(num)

    创建信号量对象

    • 参数 : 信号量的初始值
    • 返回值 : 信号量对象

    sem.acquire()将信号量减1 当信号量为0时阻塞

    sem.release()将信号量加1

    sem.get_value()获取信号量数量

    
    from multiprocessing import Process, Semaphore
    
    sem = Semaphore(3)    # 创建信号量,最多允许3个任务同时执行
    
    def rnewu():
      sem.acquire()   # 每执行一次减少一个信号量
      print("执行任务.....执行完成")
      sem.release()   # 执行完成后增加信号量
    
    
    for i in range(3):  # 有3个人想要执行任务
      p = Process(target=rnewu)
      p.start()
      p.join()

    以上就是详解Python网络进程的详细内容,更多关于python网络进程的资料请关注编程网其它相关文章!

    --结束END--

    本文标题: 详解python网络进程

    本文链接: https://www.lsjlt.com/news/10933.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • python网络编程之进程详解
      目录1.进程1.1进程:1.2在python中创建进程1.3 使用multiprocessing创建进程1.3.1 单个进程时: 1.3.2 多个子进程时:1.3.3 自定...
      99+
      2024-04-02
    • python网络-多进程(21)
      一、什么是进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,...
      99+
      2023-01-31
      进程 网络 python
    • 详解python的网络编程基础
      目录一.什么是网络编程二.socket1.socket的基本语法2.与socket有关的一些函数服务端函数客户端函数公共函数三.程序需求服务端分析客户端分析四.代码升级加上通信循环&...
      99+
      2024-04-02
    • Python进阶之网络编程
      网络通信 使用网络的目的 把多方链接在一起,进行数据传递; 网络编程就是,让不同电脑上的软件进行数据传递,即进程间通信; ip地址 ip地址概念和作用 IP地址是什么:比如192.168.1.1 这样的一些数字; ip地址的作用:用来在电...
      99+
      2023-01-31
      进阶 网络编程 Python
    • 详解Java网络编程
      目录一、网络编程1.1、概述1.2、计算机网络基础1.3、网络通信要素概述1.4、IP地址和端口号(组合就是网络套接字)1.5、网络协议1.6、三次握手与四次挥手二、TCP网络编程2...
      99+
      2024-04-02
    • Java网络编程之UDP网络通信详解
      目录1.UDP网络通信编程原理1.1 UDP网络通信编程介绍1.2 UDP网络通信编程的基本流程2.UDP网络通信编程案例1.UDP网络通信编程原理 1.1 UDP网络通信编程介绍 ...
      99+
      2024-04-02
    • Python的进程及进程池详解
      目录进程进程和程序进程的状态Python中的进程创建⼦进程全局变量问题守护进程进程池总结进程 进程是操作系统分配资源的基本单元,是程序隔离的边界。 进程和程序 程序只是一组指令的集合...
      99+
      2024-04-02
    • Java网络编程基础详解
      目录网络编程1.1 概述1.网络编程中有两个主要的问题2.网络编程中的三要素1.3、IP1.4、端口1.5、通信协议UDP协议TCP协议1.6、TCP模拟通信客户端服务器文件上传1....
      99+
      2024-04-02
    • 如何在Python中进行网络编程
      如何在Python中进行网络编程,需要具体代码示例网络编程是现代计算机科学中非常重要的一个领域,它涉及到在网络上进行数据传输和通信的技术和方法。Python是一种强大而灵活的编程语言,它具有丰富的网络编程库,使得在Python中进行网络编程...
      99+
      2023-10-22
      Python 编程 网络编程
    • C++网络编程详细讲解
      目录一、网络编程二、库示例练习一、网络编程 尽管 Boost.Asio 可以异步处理任何类型的数据,但它主要用于网络编程。这是因为 Boost.Asio 早在添加额外的 I/O 对象...
      99+
      2022-11-13
      C++网络编程 C++网络通信编程
    • Java 网络编程socket编程等详解
      网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。java.net包中J2SE的API包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。java.net包中...
      99+
      2023-05-31
      java 网络编程 socket编程
    • Python LeNet网络详解及pytorch实现
      目录1.LeNet介绍2.LetNet网络模型3.pytorch实现LeNet1.LeNet介绍 LeNet神经网络由深度学习三巨头之一的Yan LeCun提出,他同时也是卷积神经网...
      99+
      2024-04-02
    • Linux Shell网络和进程
      预计更新 1: 基础知识 简介和安装 基本命令 变量和环境变量 流程控制 条件语句 循环语句 函数 文件处理 文件读写 文件权限和所有权 文件搜索和替换 网络和进程 网络通信 进程管理 信号处理...
      99+
      2023-09-04
      linux 网络 php
    • javaEE -8(9000字详解网络编程)
      一:网络编程基础 1.1 网络资源 所谓的网络资源,其实就是在网络中可以获取的各种数据资源,而所有的网络资源,都是通过网络编程来进行数据传输的。 用户在浏览器中,打开在线视频网站,如优酷看视频,实质...
      99+
      2023-10-26
      1024程序员节 java-ee
    • C++ TCP网络编程详细讲解
      目录TCP介绍与编程流程TCP C/S架构TCP客户端编程流程1. 创建TCP套接字2. connect连接服务器3. send发送请求4. recv接收应答(默认带阻塞)5. cl...
      99+
      2024-04-02
    • 网络编程(详细)
      文章目录 网络编程的概念网络模型OSI 七层模型七层模型释义通信特点 TCP/IP 五层模型区别 网络编程的目的网络编程中的两个要素 IP 地址IP 地址类型IP 地址 `J...
      99+
      2023-09-13
      网络 服务器 网络协议
    • 网络编程—Socket套接字详解
      目录 一、网络编程 1.1、为什么需要网络编程? 1.2、什么是网络编程 1.3、发送端和接收端 ​编辑1.4、请求和响应 ​编辑1.5、客户端和服务端  二、Socket套接字  2.1、概念 2.2、分类  2.2.1、流套接字  2....
      99+
      2023-09-01
      网络 服务器 运维 Socket套接字
    • Python 网络编程
      Python 网络编程 Python 提供了两个级别访问的网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统 Socket 接口的全部方法。 高级别的网络服务模块 S...
      99+
      2023-08-31
      网络 服务器 运维
    • Python编程pytorch深度卷积神经网络AlexNet详解
      目录 容量控制和预处理读取数据集2012年,AlexNet横空出世。它首次证明了学习到的特征可以超越手工设计的特征。它一举打破了计算机视觉研究的现状。AlexNet使用了8...
      99+
      2024-04-02
    • Python网络编程
      # notes 要点网络编程 客户端/服务器架构 客户端/服务器网络编程 套接字是计算机网络数据结构。在任何类型的通信开始之前,网络应用程序必须创建套接字。可以将它们比作电话插孔,没有它将无法进行通信。 进程间通信(Inter Proc...
      99+
      2023-01-31
      网络编程 Python
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作