BABE(Blind Assignment for Blockchain Extension)协议解析

BABE协议,全称为 Blind Assignment for Blockchain Extension。BABE协议主要用在区块链系统的共识部分中,用于决定区块链应该什么时候由哪一个参与者产生区块。

BABE协议概述

在Quroboros Praos协议中,每一个协议参与者都以与其所持有股权(stake)成比例的概率被选择为区块制造者。因此,如果一个参与者所持有的股权越多则他被选为区块制造者的概率则越高。

与Quroboros Praos协议相比,在BABE中,如果各方在时间片(slot)中在线,他们应该产生一个块。然而,如果他们是离线的,那么他们将受到惩罚,在未来的时间片中被选中的机会较少。

在Ouroboros和Quroboros Praos中,最优链就是最长链。在Quroboros Genesis中,最优链可以是最长的链,也可以是在某个区间内分叉而得到的比其他链更长更密集的链。

BABE协议具有两个版本,其中第一个版本与Ouroboros Praos协议相同,而第二个版本则解决了参与方离线的相关问题。

BABE协议介绍

在BABE协议中,规定一个有序无重复的时段epochs ($e_1,e_2,…$), 并且每个时段都包含一些有序的时间片($s_1^i,s_2^i,…,s_t^i$) 直到边界值$t$. 协议在每个时段开始时将每个时间片分发给一个,多个或者零个参与者,并且这样的分配是只有在区块产生时才能被其他参与方知晓分配结果。因此协议被称为盲分配(Blind Assignment)。

每一个参与者 $P_j$ 都有至少两类密钥对:

  • 账户密钥对($sk_j^a, pk_j^a$) ,用于交易签名
  • 会话密钥对包括用于VRF函的密钥对($sk_j^{vrf}, pk_j^{vrf}$)以及用于区块签名的密钥对($sk_j^{sgn}, pk_j^{sgn}$)。

VRF密钥可以长期存在,但相关的签名密钥应不断更新,以保证转发安全,防止攻击者造成破坏。

对于每一个参与者$P_j$ :

  • 保留一组本地区块链 $C_j=\{C_1,C_2,…,C_l\}$。这些链有一些共同的区块(至少起源区块(genesis block)相同),直到某个长度。
  • 有一个本地缓冲区,其中包含了用账户密钥签署的交易,这些交易将被添加到区块上

GRANDPA Validators 参与下的BABE协议 $\approx$Ouroboros Praos协议

将参与方被选为造块者的概率如下定义:

其中$\alpha_i$为参与方$P_i$ 所具有的股权(stake),$c$ 是一个常数,函数$\phi$ 具有独立聚合的特点,即被选为时间片主导者的概率不会因为参与者将他的股权分摊到各个虚拟参与者而增加。

对于 $P_i$的阈值为

其中$l_{vrf}$为VRF的第一次输出值的长度。

BABE协议的三个阶段

1. 创世阶段

  • 手动生成唯一的创世块
    • 其中包含一个随机数$r_1$,用于在第一个时间段用于分配时间片主导者,并且我们可能会设置$r_1 = 0$或者使用洋葱路由网络中的公共随机数来代替。
  • 初始股权持有人($st_1, st_2, …,st_n$)的初始股权以及他们的会话公钥和账户公钥。

2. 一般阶段

  • 每个时间片主导者都应该产生并发布一个区块。而其他节点则试图用他们观察到的新的有效块来扩展更新他们的链。

  • $P_j$在当前时间片$sl_k$中$中有一组链$$\mathbb{C}_j$。通过我们的选择方案,在$sl_{k-1}$中选择了一条最佳链$C$,$C$的长度为$l-1$。

  • 如果下面VRF的第一个输出($d$)小于阈值$\tau_j$,那么他就是时间片主导者。所以,$P_j$的股权越多,他就越有机会被选为slot leader。

  • 生成或验证

    • 当$P_j$生成一个区块。该区块应该包含槽号、前一个区块的哈希值、VRF输出、交易和签名$\sigma = Sign_{sk_j^{sgn}}(sk_j||H_{j-1}|||d|||\pi||tx)$
    • 当$P_j$收到一个由$P_t$制作的块$B=(sl,H,d^{‘},\pi^{‘},tx^{‘},\sigma^{‘})$时,它用$Validate(B)$验证该块。
      • 是否$Verify_{pk_t^{sgn}}(\sigma^{‘})\rightarrow valid$。
      • 是否有$sl\leq sl_k$。
      • 如果当事人是slot leader,是否满足$Verify_{pk_t^{vrf}}(\pi^{‘},r_m||sl)\rightarrow valid$且$d^{‘}<\tau_t$.
      • $P_t$是否在槽$sl$中没有为另一条链产生另一个块(没有双签名)。
      • 是否存在一个带有头部$H$的链$C^{‘}$。

3. 时间段(Epoch)更新

如果参与者想要更新他在epoch $e_{m+1}$的股权,那么他的股权应该在 $e_m$的开始(epoch $e_{m-1}$的末尾)更新。因为我们不希望各参与者在看到$e_{m+1}$的随机性后调整自己的股权。

新时间段的随机性的计算方法与Ouroboros Praos相同。将从时间段的第一个时间片开始到$e_m$的$2R/3^{th}$个时间片($R$是时间段大小)所有的VRF输出以块的形式连接起来。假设连接后为$\rho$。那么下一个时间段的随机性则为:

这也可以结合VDF输出,防止敌手的小偏向,以获得更好的安全界限。

最佳链选择算法(Best Chain Selection)

给定一个链集$\mathbb{C}_j$和参与者当前的本地链$\mathbb{C}_{loc}$,最佳链算法通过GRANDPA消除所有不包括最终确定的块B的链。我们用集合$C_j^{‘}$来表示剩余的链。然后算法输出包含$\mathbb{C}_{loc}$中不超过k个块的最长链。它抛弃从$\mathbb{C}_{loc}$分叉超过k个时间片的链。

我们没有使用Ouroboros Genesis中的选链规则,因为这个规则对于参与方经过一段时间后才上线,并且没有任何与当前有效链相关的信息来说是有用的(对于y一直在线的参与方来说,Genesis规则和Praos是无法区分的,概率可以忽略不计)。因为GRANDPA的不可改变性(finality),新来的参与方有了一个参考点来建立他们的链,所以我们不需要创世规则。

相对时间(Relative Time)

各个参与者知道当前的时间片对于BABE的安全性和完整性非常重要。我们假设时间片的时间$T$大于网络传播时间。

每一个参与者都有一个本地时钟,这个时钟不必与网络同步。当他接收到创世块时,它将到达时间保存为$t_0$,作为第一个时间片开始的参考点。我们知道第一个时间片的开始时间对每个人来说都不一样。我们假设这个时差与时间片时间$T$相比可以忽略不计。

现在,我们考虑在创世块发布后加入BABE的各个参与者,因为这些参与者在加入时不知道当前的时间片。这些人必须等待至少$δ$个有效区块,以确定时间片开始和结束的大约时间。

假设一个参与者$P_j$加入网络,然后决定最佳链$C$,其header产生在时间片$sl_u$中。$P_j$存储在$sl_u$之后块的到达时间。用$t_1,t_2,…,t_δ$表示存储的区块到达时间,对应的区块包括时间片号(slot number) $sl′_1,sl′_2,…,sl′_δ$。这些时间片序号不一定是连续的,因为有些时间片可能是空的,或者slot leader是离线的。$P_j$推断出当前时间片序号$sl=sl′_δ+1$,给定 $T_i=T(sl-sl′_i)$为一个包括$\{t^′_1+T_1,t^′_2+T_2,…,t^{′}_δ+T_δ\}$的时间区间。

另一个关键的时间问题是 “When to release the block”。为了找到一个好的区块释放时间,使其在时段结束前到达每个人,每个参与方都遵循与新加入的参与方相同的策略。假设参与者$P_j$是$sl$中的slot leader,并且计算如上所述的$T_{sl}=\{t′_1+T_1,t′_2+T_2,…,t′_δ+T_δ\}$。那么函数$f_{time}:R^δ→R$给出给定$T_{sl}$作为输入的释放时间。

$f_{time}$的可能候选值为平均值、最大值等等。

然后参与者在时间$f_{time}-L_{avg}$释放区块,其中$L_{avg}$是估计的网络延迟。

Embedded Operating systems Review

The First:Task management

How to describe a task?

1.A task is a excutable software entity specialized to perform a special action (Enemy Detect, Danger Evaluation, Missile Launching…), which includes a sequential code, and relative data and computer resource.

2.A task is an infinite loop function. it looks just like any other C function containing a return type and an argument, but it must never return. The return type of a task must always be declared to be void

What’s the difference between task and process?

int i=1;
test(){
    sleep(10s); //等待main线程执行完i++
    printf("%d", i);
}

int main(){
    create_task(test,............);
    i++;
}

《Embedded Operating System》P20:if create_task is the API of creating thread,the result of test() is 2;if it is the API of creating process, the result is 1.(多线程时,进程之间不能相互访问对方的变量)

How to create a task in uc/OS II?

OsTaskCreate

INT8U OStaskCreate(void(*task)(void *p_arg), void *p_arg, OS_STK *ptos,INT8U prio //prio由工程规划决定一个任务的优先级){
    OS_STK *psp;
    INT8U err;
    #if OS_CRITICAL_METHOD == 3    //第三种方式进入临界区
    OS_CPU_SR cpu_sr = 0;
    #endif
        OS_ENTER_CRITICAL();        //关中断
        if(OSIntNesting >0){        //判断是否发生中断嵌套
            OS_EXIT_CRITICAL();     //开中断
            return (OS_ERR_TASK_CREATE_ISR);    返回错误
        }
    if(OSTCBPrioTbl[prio] == (OS_TCB *)0){  //判断任务优先级是否被占用
        OSTCBPrioTbl[prio] = (OS_TCB *)1;
        OS_EXIT_CRITICAL();                 //关中断
    }

    //任务堆栈初始化
    psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos,0);
    //TCB初始化        
    err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
    if(err==OS_NO_ERR){
        OS_ENTER_CRITICAL();
        OSTaskCtr++;
        OS_EXIT_CRITICAL();
        if(OSRuning == TRUE){
            OS_Sched();         //进入调度点
        }
        else{
            OS_ENTER_CRITICAL();
            OSTCBPrioTbl[prio] = (OS_TCB *)0;   //TCB初始化错误释放占用优先级
            OS_EXIT_CRITICAL();
        }
        return (err);
    }
    OS_EXIT_CRITICAL();
    return (OS_PRIO_EXIST);
}

OSTaskStkInit

OS_STK *OSTaskStkInit (void(*task)(void *pd),void *p_arg,OS_STK *ptos,INT16U opt){
    OS_STK *stk;                                                         
    opt    = opt; 
    stk    = ptos;                                  
    *(stk)  = (OS_STK)task;       
    /* Entry Point*/                               
    *(--stk) = (INT32U)0;   
    /*LR */                                     
    *(--stk) = (INT32U)0;
    /*R12*/                                     
    *(--stk) = (INT32U)0;           /*R11*/
    *(--stk) = (INT32U)0;           /*R10*/
    *(--stk) = (INT32U)0;           /*R9*/
    *(--stk) = (INT32U)0;           /*R8*/
    *(--stk) = (INT32U)0;           /*R7*/
    *(--stk) = (INT32U)0;           /*R6*/
    *(--stk) = (INT32U)0;           /*R5*/
    *(--stk) = (INT32U)0;           /*R4*/    
    *(--stk) = (INT32U)0;           /*R3*/
    *(--stk) = (INT32U)0;           /*R2*/
    *(--stk) = (INT32U)0;           /*R1*/
    *(--stk) = (INT32U)p_arg;       
    *(--stk) = (INT32U)0x 0x00000013L;                                
    return (stk);
}

OS_TCBInit

INT8U  OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt){
    #if OS_CRITICAL_METHOD == 3                               
        OS_CPU_SR  cpu_sr;
    #endif    

    OS_TCB    *ptcb;
    OS_ENTER_CRITICAL();
    ptcb = OSTCBFreeList;   

    if (ptcb != (OS_TCB *)0) {
        OSTCBFreeList = ptcb->OSTCBNext;          
        OS_EXIT_CRITICAL();
        ptcb->OSTCBStkPtr= ptos;                      
        ptcb->OSTCBPrio = (INT8U)prio;                
        ptcb->OSTCBStat = OS_STAT_RDY;               
        ptcb->OSTCBDly = 0;                        
        pext = pext;                      
        stk_size = stk_size;
        pbos = pbos;
        opt = opt;
        id = id;
       //Priority Bitmap
        ptcb->OSTCBY = prio >> 3;                             
        ptcb->OSTCBBitY= OSMapTbl[ptcb->OSTCBY];
        ptcb->OSTCBX =  prio & 0x07;
        ptcb->OSTCBBitX=  OSMapTbl[ptcb->OSTCBX];
        OS_ENTER_CRITICAL();
        OSTCBPrioTbl[prio] = ptcb;
        ptcb->OSTCBNext = OSTCBList;                  
        ptcb->OSTCBPrev = (OS_TCB *)0;
        if (OSTCBList != (OS_TCB *)0) {
            OSTCBList->OSTCBPrev = ptcb;
        }
        OSTCBList = ptcb;
        OSRdyGrp |= ptcb->OSTCBBitY;        
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
        OS_EXIT_CRITICAL();
        return (OS_NO_ERR);
    }
    OS_EXIT_CRITICAL();
    return (OS_NO_MORE_TCB);
}

How to schedule tasks in the ready queue?

OS_Sched

void OS_Sched(void){
    #if OS_CRITICAL_METHOD == 3
        OS_CPU_SR cpu_sr = 0;
    #endif

    OS_ENTER_CRITICAL();
    if(OSIntNesting == 0){
        if(OSLockNesting == 0){
            OS_SchedNew();     //函数内部构造见下一标题           
            if(OSPrioHighRdy != OSPrioCur){
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
                #if OS_TASK_PROFILE_EN >0
                    OSTCBHighRdy-> OSTCBCtxSwCtr++;
                #endif
                    OSCtxSwCtr++;
                    OS_TASK_SW();
            }
        }
    }
    OS_EXIT_CRITICAL();
}

OS_SchedNew(以下涉及优先级位图法)

#if OS_LOWEST_PRIO <= 63
    INT8U y; //采用优先级位图法获取最高优先级任务
    y= OSUnMapTbl[OSRdyGrp];
    OSPrioHighRdy = (INT8U)((y << 3)) + OSUnMapTbl[OSRdyTbl[y]]);
#else
    INT8U y;
    INT16U *ptbl;
    if((OSRdyGrp & 0xFF)!=0){
        y = OSUnMapTbl[OSRdyGrp & 0xFF];
    } else {
        y = OSUnMapTbl[(OSRdyGrp >> 8) & 0xFF] + 8;
    }

    ptbl = &OSRdyTbl[y];
    if((*ptbl & 0xFF) !=0){
        OSPrioHighRdy = (INT8U)((y << 4) + OSUnMapTbl[(*ptbl & 0xFF)]);
    }else{
        OSPrioHighRdy = (INT8U)((y << 4) + OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8);
    }
#endif
}

How to switch tasks on (uc/OS II + ARM 2440)?

When a kernel decides to run a different task, it simply saves the current task’s context (CPU registers) in the current task’s context storage area (Current task’s stack). Once this operation is performed, the new task’s context is restored from its storage area (New task’s stack) and then resumes execution of the new task’s code. This process is called a context switch.

OSCtxSw:(以下涉及ARM汇编指令)

OSCtxSw:
    STMFD   SP!,{LR}        ;PC
    STMFD   SP!,{R0-R12,LR} ;R0-R12 LR
    MRS     R1, CPSR        ;Push CPSR
    STMFD   SP!,{R0}       

    ;----------------------------------------------------------------------
    ;OSTCBCur->SOTCBStkPtr = Sp
    ;----------------------------------------------------------------------
    LDR     R0, =OSTCBCur
    LDR     R0,[R0]
    STR     SP!,[R0]

    BL      OSTaskSwHook
    ;----------------------------------------------------------------------
    ; OSTCBCur = OSTCBHighRdy;
    ;----------------------------------------------------------------------
    LDR     R0, =OSTCBHighRdy
    LDR     R1, =OSTCBCur
    LDR     R0, [R0]
    STR     R0, [R1]

    ;----------------------------------------------------------------------
    ; OSPrioCur = OSPrioHighRdy;
    ;----------------------------------------------------------------------
    LDR     R0, =OSPrioHighRdy
    LDR     R1, =OSPrioCur
    LDRB    R0, [R0]
    STRB    R0, [R1]

    ;----------------------------------------------------------------------
    ;  OSTCBHighRdy->OSTCBStkPtr;
    ;----------------------------------------------------------------------
    LDR     R0, =OSTCBHighRdy                                            
    LDR     R0, [R0]
    LDR     SP, [R0]
    ;----------------------------------------------------------------------
    ;Restore New task context
    ;----------------------------------------------------------------------
    LDMFD              SP!, {R0}    ;POP CPSR       
    MSR                SPSR_cxsf,R0
    LDMFD              SP!, {R0-R12, LR, PC}^

Critical section accessing during scheduling(进入临界区段的三种方法)

#if  OS_CRITICAL_METHOD == 1 
    #define  OS_ENTER_CRITICAL()   (Cli()) 
    #define  OS_EXIT_CRITICAL()      (Sti())  

#elseif   OS_CRITICAL_METHOD == 2  
    #define  OS_ENTER_CRITICAL()   (PushAndCli())  
    #define  OS_EXIT_CRITICAL()     (Pop()) 

#elseif   OS_CRITICAL_METHOD == 3  
    #define  OS_ENTER_CRITICAL()  (cpu_sr = OSCPUSaveSR())
    #define  OS_EXIT_CRITICAL()   (OSCPURestoreSR(cpu_sr))
#endif

Priority Bitmap

How to get the highest priority from the “ready” tasks?
#if OS_LOWEST_PRIO <= 63  
    INT8U  y;
    y= OSUnMapTbl[OSRdyGrp];           
    OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); 

The Second: Interruption (Event triggered mechanism)

Classfication of interruption

  • Interruption
    • 1.Outside interruption
    • 2.Hard interruption
  • Trap
    • 1.Inside interruption (INT 21H,SWI ?)
    • 2.Soft interruption
  • Exception

Case1: Interruption on (ARM 2440 + uc/OS II)

中断上下文切换.jpg

interruption vector

Type Address
FIQ 0x1C
IRQ 0x18
(Reserved) 0x14
Data Abort 0x10
Prefetch Abort 0x0C
Software Interrupt 0x08
Undefined Instruction 0x04
Reset 0x00

IRQ

IRQ
    LDR     r0, =INTOFFSET      //基于中断向量表获取INTOFFSET
    LDR     r0, [r0]
    LDR     r1, =HandleEINT0    //IRQ中断基址
    LDR     pc, [r1, r0 lsl #2] //左移两位,因为一个地址占用四个单位空间

How to save the information of the interrupted program?

IRQ
    SUB     lr, lr,#4
    STMFD   sp!,{r0.r12, lr}
    MRS     r0,spsr
    STMFD   sp!,{r0}
    LDR     r0, =INTOFFSET          ;10: 0x28
    LDR     r0, [r0]
    LDR     r1, =HandleEINT0
    ADD     r1, r1, r0, lsl #2
    LDR     r1,[r1]
    MOV     lr, pc                  ;Why not (pc-4): pc -> LDMFD sp!,{r0}
    MOV     pc, R1                  ;跳转至中断子程序执行
    LDMFD   sp!,{r0}
    MSR     spsr_cxsf,r0
    LDMFD   sp!,{r0.r12,lr}
    MOVS    pc,lr

How to process the IRQ if uc/OS II is running?

IRQ
    STMDB   sp!,{r0-r2}                      ;sp_irq 
    MOV     r0,sp 
    ADD     sp,sp,#12 
    SUB     r1,lr,#4                         ;lr_irq 
    MRS       r2,spsr

    MSR     cpsr_cxsf,#SVCMODE|NOINT  
    STMDB   sp!,{r1}                         ;sp_svc 
    STMDB   sp!,{r3-r12,lr} 
    LDMIA   r0!,{r3-r5}   
    STMDB   sp!,{r2-r5}

    //中断嵌套变量的汇编代码(不要求掌握)
    LDR     r0,=OSIntNesting 
    LDR     r1,[r0]
    ADD     r1,r1,#1 
    STRB    r1,[r0]
    TEQ     r1,#1
    BNE     %F1  

    LDR     r0,=OSTCBCur 
    LDR     r0,[r0]
    STR     sp,[r0]
    MSR     psr_c,#IRQMODE|NOINT
    LDR     r0,= INTOFFSET                  ;10:    0x28
    LDR     r0,[r0]
    LDR     r1,=HandleEINT0                 ;0x33FFFF20

    MOV     lr,pc 
    LDR     pc,[r1,r0,lsl#2]
    MSR     cpsr_c,#SVCMODE|NOINT 
    BL      OSIntExit

    LDR     r0,[sp],#4
    MSR     spsr_cxsf,r0 
    LDMIA   sp!,{r0-r12,lr,pc}^

How to process context switch triggered by the IRQ?

void  OSIntExit (void)  {
    #if OS_CRITICAL_METHOD == 3
        OS_CPU_SR  cpu_sr;
    #endif

    if (OSRunning == TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0) {     
        OSIntNesting--;
        }
        If ((OSIntNesting == 0) && (OSLockNesting == 0)) {
            OSIntExitY= OSUnMapTbl[OSRdyGrp];
            OSPrioHighRdy=(INT8U)((OSIntExitY<<3)+ OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
            if (OSPrioHighRdy != OSPrioCur) {
                OSTCBHighRdy= OSTCBPrioTbl[OSPrioHighRdy];
                OSCtxSwCtr++;
                OSIntCtxSw(); 
            }    
        }    
    OS_EXIT_CRITICAL();
    }    
}

Case2: Interruption of ARM11MPCORE

  • Distributed Interrupt Controller
    • 1.Interrupt Distributor
    • 2.CPU Interfaces
  • Generic Interrupt Controller
  • Respond the interruption
  • Initialize the interruption

The third: Time management(Time triggered mechanism)

  • Time management is important
  • Real-time Clock(RTC)
  • System Clock(Timer)
  • Size of system clock
    • Tick(the period of output impulse generated by the timer)
  • Time management
    • Task Delay(uc/OS II: osTimeDly())
    • Difference time chain
    • Watchdog timer
    • System clock ISR

OSTimeDly

void  OSTimeDly (INT16U ticks){                                        
#if OS_CRITICAL_METHOD == 3
      OS_CPU_SR  cpu_sr;
#endif    
1
2
3
4
5
6
7
8
9
if (ticks > 0){ 
OS_ENTER_CRITICAL();
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0){
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; }
OSTCBCur->OSTCBDly = ticks;
OS_EXIT_CRITICAL();
OS_Sched();
}
}

Django项目部署过程记录及部署教程

使用Nginx+Gunicorn+Virtualenv+Supervisor在CentOS7(Python/2.7)上部署Django项目

部署工具版本信息:

Nginx/1.12.2 Gunicorn:19.9.0 Virtualenv/15.1.0 Supervisor/3.1.4

Django项目版本消息:

Django/2.23 Python/3.5.4

项目开发调试一般采用的是Pycharm+mysql(wamp),因此没有遇到过多配置问题。当要把项目迁移到web服务器主机上时,就会遇到很多的困难,其中最棘手的就是配置问题。

接下来将介绍如何使用Nginx(Web服务器)+Gunicorn+Virtualenv+Supervisor来部署Django项目

第一步:安装Python3解释器

在我们的Django项目中,采用的是Python3,而大多数的服务器提供商,提供的linux版本中默认的python环境还是python2,同时,linux中的许多服务也是基于python2(此处是部署的第一坑个坑,可能让你不断的重置系统)。因此,我们需要在服务器中安装Python3解释器。

1.下载源码压缩文件

安装解释器的第一步就是去Python官网下载Python解释器的源码,这里下载的是Python3.6.8版本源码压缩包

接下来把压缩包上传到Centos7系统的/usr/local/src 目录下,然后切换到该目录下,执行解压缩命令:
tar --zxvf Python-3.6.8.tgz
解压完成后进入Python-3.6.8目录下

2.解决编译环境问题

因为编译时需要gcc等环境,所以先安装相关环境:yum -y install gcc* glibc*

3.编译安装

运行脚本文件,对安装进行配置:
./configure --prefix=/usr/local/python3.6.8 --with-ssl
接着开始编译安装make && male install
安装完成后进入python3.6.8目录下cd /usr/local/python3.6.8/
可以看到目录中的四个文件夹bin、include、lib、share

4.运行Python解释器

安装完成后cd bin && ./python3 以检查是否安装成功

5.软链接(选做部分)

软链接实现在任意路径下输入python3进入python3的环境
ln -s /usr/local/python3.6.5/bin/python3 /usr/local/bin/python3
pip 命令也需要做一下软链接,就可以在任意目录下,用 pip3 命令安装各种模块和包了
ln -s /usr/local/python3.6.5/bin/pip3 /usr/local/bin/pip3

第二步:安装Virtualenv

第三步:配置Gunicorn

第四步:安装Supervisor

第五步:配置Django