用汇编语言实现窗口

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用汇编语言实现窗口相关的知识,希望对你有一定的参考价值。

不是win32汇编噢,注意,是dos下,利用图形来做,记得97年编程大赛冠军是
这样一个程序
http://hi.baidu.com/%CD%C7%B7%CF%5F%D0%A1%C5%D6/blog/item/aa2f6035c96998335ab5f566.html
用汇编连如此复杂的程序都可以实现,实现窗口应该很简单吧。
早说了不是win32汇编了 就调用一个api可以办到的事情我不会拿出来问的
例程我已经贴到贴吧了
http://tieba.baidu.com/f?kz=573764537
如果真要论分值,这题值1000分以上,我是希望各位能通过此题加强自己对于dos图形的理解。

呵呵,其实也是很简单的,建议你去看看 罗云斌的 

Windows环境下32位汇编语言程序设计(第2版) 

里面揭开了win32汇编编程的神秘面纱 

这里是个简单的参考程序 

; Hello.asm 

; 使用 Win32ASM 写的 Hello, world 程序 

; 使用 nmake 或下列命令进行编译和链接: 

; ml /c /coff Hello.asm 

; Link /subsystem:windows Hello.obj 

.386 

.model flat,stdcall 

option casemap:none 

; Include 文件定义 

include windows.inc 

include user32.inc 

includelib user32.lib 

include kernel32.inc 

includelib kernel32.lib 

; 数据段 

.data 

szCaption db 'A MessageBox !',0 

szText db 'Hello, World !',0 

; 代码段 

.code 

start: 

invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK 

invoke ExitProcess,NULL 

end start

运行的效果是:

参考技术A 没必要跟机器指令较劲吧。

人家是使用高级语言编译成的机器指令背下来而已

就算是高手他们背机器指令没有理由的。

都是用C语言活着C++做的程序然后

反汇编背下了机器指令
参考技术B 是的,实现窗口不难,就是很麻烦的。
每一个点都要自己画出来。
参考技术C .model small
.stack
.code
esc_key equ 1bh
win_ulc equ 10
win_ulr equ 10
win_lrc equ 60
win_lrr equ 20
win_width equ 50
main proc far
call clear_screen
locate: mov ah,2
mov dh,win_ulr
mov dl,win_ulc
mov bh,0
int 10h
mov cx,win_width
get_char:mov ah,1
int 21h
cmp al,esc_key
jz exit
loop get_char
mov ah,7
mov al,2
mov ch,win_ulr
mov cl,win_ulc
mov dh,win_lrr
mov dl,win_lrc
mov bh,7
int 10h
jmp locate
exit: mov ax,4c00h
int 21h
main endp
clear_screen proc near
push ax
push bx
push cx
push dx
mov ah,6
mov al,0
mov bh,7
mov ch,0
mov cl,0
mov dh,24
mov dl,79
int 10h
mov dx,0
mov ah,2
int 10h
pop dx
pop cx
pop bx
pop ax
ret
clear_screen endp
end main
参考技术D 用图形来实现窗口是很简单,只是数据量太大,连贴吧都放不下,
楼主如想要,我可以再在窗口中画个美眉,把程序发到你的邮箱。
贴吧的程序,汉字显示部分还可再简洁些。本回答被提问者采纳

一种在C语言中用汇编指令和 System V ucontext 支撑实现的协程切换

1 实现内容

此文在看了 python yieldyield from 机制后,觉得这种轻量级编程技术在小并发场景下优雅可爱,在大并发场景下较进程或线程而言能突破资源瓶颈,实在令人忍不住而想在C语言中实现一个。

经过一些学习后,此文在 Linux 上用C语言实现了一个。目前具体包括

[1] co_yield() —— 类似 python 的 yield,用于协程切换;
[2] co_send()  —— 类似 python 生成器中的 send(),用于开始或恢复协程的运行;
[3] co_yield_from() —— 类似 python 的 yield from,用于同步基于 co_yield() 切换的协程;
[4] co_loop() —— 略似于 python 的 asyncio.loop(),用于协程并发调度。

e.g.
/**
 ** brief: suspending current coroutine related 
    with ci then switch to specific coroutine 
    when co_yield() called. 
 ** param: ci, bears control-information for 
               corresponding coroutine.
 ** return: zero returned if succeeded, 
    otherwise errno. 
 ** sidelight: co_yield() just like the 'yield' 
    in python. */
extern int 
co_yield(ci_s *ci);

彩蛋:在此文最后一节进行 python yieldyield from 原理粗探,先进入与实现相关的主题。

此文所编写的C协程切换程序层次结构体大体如下。

简单吧^_^

assembly | ucontext

2 基石——协程上下文及切换实现

协程的基石是协程上下文及切换,此文用两种方式来支撑实现。

2.1 用System V ucontext支撑协程的上下文和切换

此文先不从头开始,先看看有没有描述协程上下文及切换的现成 C 库。于是找到 System V ucontext。通过阅读 ucontext 手册,用 “ucontext一族”实现一个在 C 环境下的类似于 yieldyield from 机制的协程切换应该不成问题。

(1) 理解 ucontext

根据手册理解下 ucontext 数据数据结构体和相关函数吧。

#include <ucontext.h>

/* 描述协程上下文的结构体类型 ucontext_t 至少包含以下成员 */
typedef struct ucontext_t 
    /* uc_link,由 makecontext() 
       创建协程运行结束后,程序
       切换到 uc_link 所指协程上
       下文处运行,uc_link 为 NULL 
       时则整个线程退出。*/
    struct ucontext_t *uc_link;
    
    /* 用于记录在当前协程中所需屏蔽的信号 */
    sigset_t          uc_sigmask;
    
    /* 协程栈空间 */
    stack_t           uc_stack;
    
    /* 协程上下文,主要用于存储协程运行涉及的寄存器状态 */
    mcontext_t        uc_mcontext;
    ...
 ucontext_t;

/**
 ** 功能:将程序当前协程级上下文保存
    到 ucp 指向的类型为 ucontext_t 的
    结构体中。
    
 ** 返回值:执行成功返回0;执行
    失败时将错误码保存在 errno 
    变量中并返回-1。*/
int getcontext(ucontext_t *ucp);

/**
 ** 功能:用 func 地址处的协程上下文修改
    由 getcontext() 在 ucp 所指结构体中
    创建的协程上下文。makecontext() 支持
    向 func 地址(函数)传递 argc 个 int 
    类型参数。
    
 ** 注:在调用 makecontext() 之前,必须为 
    ucp->uc_stack 分配内存用作协程运行栈,
    并为 ucp->uc_link 指定 ucp 对应协程运行
    结束后 将切换运行协程的协程上下文。*/
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

/**
 ** 功能:将当前协程上下文保存在 oucp 所指
    结构体中,并跳转执行 ucp 所指协程上下文处。
    
 ** 返回值:swapcontext() 执行成功时暂不返回
    (后续由 oucp 成功切换回来时,该函数会返回0);
    执行失败时将错误码保存在 errno 变量中随后返回-1。
    errno 为 ENOMEM时 表明所设置栈内存已不足。*/
int  swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
(2) 协程初次运行

由于 ucontext 已经包含了对协程上下文的描述及切换,所以只需在协程初次运行时进行一些初始化,包括对 ucontext 数据结构体和协程参数的初始设置。

int 
co_start_uc(ci_s *ci)

    int ret;
    void   *arg  = NULL;
    cctx_s *ctx  = NULL;
    cctx_s *bctx = NULL;
    
    ctx = co_cctx(ci);
    ret = getcontext(ctx);
    IF_EXPS_THEN_RETURN(ret, errno);

    bctx = co_bcctx(ci);
    ctx->uc_link = bctx;
    ctx->uc_stack.ss_sp   = co_stack(ci);
    ctx->uc_stack.ss_size = co_ssize(ci);
    arg = co_arg(ci);
    /**
     * there's not matter if ci->co's type 
     * is not func_t, as long as ci->co 
     * wouldn't achieve more parameters than 
     * makecontext() passed. */
    typedef void (*func_t)(void);
    makecontext(ctx, (func_t)co_cofn(ci), 4, 
        (uint32_t)((uintptr_t)ci), 
        (uint32_t)((uintptr_t)ci >> 32), 
        (uint32_t)((uintptr_t)arg), 
        (uint32_t)((uintptr_t)arg >> 32) );
    ret = swapcontext(bctx, ctx);
    IF_EXPS_THEN_RETURN(ret, errno);

    return ret;

在初始化 ucontext 数据结构体和协程参数即调用 swapcontext 将当前的协程上下文保存在 ci 的成员中后,调用 swapcontext 跳转执行协程函数,待协程函数调用 co_yield() 时协程函数将挂起而返回到调用 swapcontext 处——主线程继续运行。

ci_s 结构体 和 co_yield() 后续有介绍。

2.2 用汇编指令支撑协程的上下文和切换

(1) 了解协程上下文涉及内容

在C语言中,一个协程可用一个函数来表示,那么协程上下文即一个函数执行所涉及的上下文为——与栈、指令指针关联的寄存器,以及函数调用约定涉及的寄存器。

此文不知晓更标准的称呼,就以“gcc convention on __i386”和“gcc convention on __amd64”来分别代表 Linux 上32位C程序和64位C程序中函数的调用约定吧。
[1] gcc convention on __i386

ebp, ebx, esi, edi - 由被调用函数保证其值不变;

默认用栈传递参数,如
void fn(int a, int b);

在父函数中调用 fn(1, 2); 时传参方式为
push 2 --> b
push 1 --> a
即从右往左将实参依次入栈。

可以指定前3个参数以寄存器的方式传递,以
extern void __attribute__ ((__noinline__, __regparm__(2)))
fn(int a, int b) 
声明fn时,形参a和b的值将分别由eax和edx两个寄存器传递。

[2] gcc convention on __amd64

rbp, rbx, r12, r13, r14, r15 - 由被调用函数保证其值不变;

前6个参数默认用寄存器传递,后续参数用栈传递,如
void fn(uint32_t a, uint32_t b, 
        uint32_t c, uint32_t e, 
        uint32_t f, uint32_t g, 
        uint32_t h);
      
在父函数中调用fn(1, 2, 3, 4, 5, 6, 7)时
edi=1  --> a, esi=2 --> b, edx=3 --> c, 
ecx=4  --> e, r8d=5 --> f, r9d=6 --> g,
push 7 --> h
(2) 用汇编指令支撑协程切换和协程传参

[1] 协程切换
由于C编译器会往C函数中添加维护栈帧的指令,所以协程切换函数必须由汇编指令来完成。若不想涉及汇编程序的编译和链接,可以在C程序中使用 asm 关键字告知编译器将汇编指令嵌在C程序中。

来吧,尝试按照协程上下文涉及信息编写该协程切换函数。

__asm__(
    "\\t.globl co_switch_asm\\n"
    "co_switch_asm:\\n"
    
    /* according to the call 
       convention:
       
       ebp, ebx, esi, edi need
       called-function to backup
       on i386;
       
       rbp, rbx, r12, r13, r14, r15
       need called-function to backup
       on amd64. */
    #if __i386
        "pushl %ebp\\n\\t"
        "pushl %ebx\\n\\t"
        "pushl %esi\\n\\t"
        "pushl %edi\\n\\t"

        /* backup current stack-top
           to first(left-most) argument,
           then assign co-stack-top to esp.
           
           see: declaration of 
           co_switch_asm 
           in ln_context.h */
        "movl %esp, (%eax)\\n\\t"
        "movl (%edx), %esp\\n\\t"
        
        "popl %edi\\n\\t"
        "popl %esi\\n\\t"
        "popl %ebx\\n\\t"
        "popl %ebp\\n\\t"
         
         /* switching. 
            the target-address in stack 
            by call or co_start_asm. */
        "popl  %ecx\\n\\t"
        "jmpl *%ecx\\n\\t"

    #elif __amd64
        "pushq %rbp\\n\\t"
        "pushq %rbx\\n\\t"
        "pushq %r12\\n\\t"
        "pushq %r13\\n\\t"
        "pushq %r14\\n\\t"
        "pushq %r15\\n\\t"

    /* I want to save _CORET here
       by instructions just like
       'pushq _CORET'(etc.)to accept 
       coroutine return. unfortunately 
       fail.

       so coroutines which use CCTX_ASM
       to support coroutine-switching 
       must use co_end() to terminate 
       itself before return.

       the same situation on __i386, 
       please do a favor to accept the
       'return' statement of coroutine
       if you owns the same faith. */

        /* same meaning as i386.
           see _co_arg_medium for 
           the argument-passing 
           convention. */
        "movq %rsp, (%rdi)\\n\\t"
        "movq (%rsi), %rsp\\n\\t"

        "popq %r15\\n\\t"
        "popq %r14\\n\\t"
        "popq %r13\\n\\t"
        "popq %r12\\n\\t"
        "popq %rbx\\n\\t"
        "popq %rbp\\n\\t"
        
        /* switching. 
           same meaning as i386. */
        "popq  %rcx\\n\\t"
        "jmpq *%rcx\\n"
        "_CORET:"

    #else
        #error "coroutine-context unsupported"
               " on current architecture"
    #endif
);

用汇编指令实现的 co_switch_asm 可以用于协程切换,但无法完成协程参数的传递以及接受协程中 return 语句。此文解决了第一个问题——第二个问题在此文结束后也未得到解决(快来指点啊^_^)。

[2] 协程传参
由于不是拷贝粘贴式编程,初次解决该问题还是有一定难度。不过这次皇天不负有心人,经此文思前想后得到一个解决办法——用中间函数来传递协程参数,同 co_switch_asm,这个中间函数也只能用汇编指令实现。

void 
_co_arg_medium(void);

/** 
 * because of the instructions on 
 * stack-frame would be automatically 
 * added by C-compiler in C-functions, 
 * those routines can't be 
 * inline-assembly in C-function.
 *
 * @reality: the routines on __i386 
 *  not tested by me. */
__asm__  (
    "\\t.globl _co_arg_medium\\n"
    "_co_arg_medium:\\n"
    /* the arguments co_fn, arg, ci 
       prepared by co_start_asm */
    #if   __i386
        "popl %eax\\n\\t" // get co_fn
        "popl %ecx\\n\\t" // get arg
        "popl %edx\\n\\t" // get ci

    /* call convention on arguments 
       of gcc on __i386 e.g.

       void fn(int a, int b);

       fn(1, 2);
       passing argument by stack 
       in caller:
       push 2 --> b
       push 1 --> a */
        "movl $0, %esi\\n\\t"
        "pushl %esi\\n\\t"
        "pushl %ecx\\n\\t"
        "pushl %esi\\n\\t"
        "pushl %edx\\n\\t"
    #elif __amd64
        "popq %rax\\n\\t" // get co_fn
        "popq %rsi\\n\\t" // get arg
        "popq %rdi\\n\\t" // get ci

    /* call convention on arguments 
       of gcc on __amd64 e.g.
       void fn(uint32_t a, uint32_t b, 
                uint32_t c, uint32_t e, 
                uint32_t f, uint32_t g, 
                uint32_t h);
      
      passing arguments by registers 
      and stack in caller:
      edi --> a, esi --> b, edx --> c, 
      ecx --> e, r8d --> f, r9d --> g,
      push real h --> formal h */
        "movl %esi, %edx\\n\\t"
        "movq %rsi, %rcx\\n\\t"
        "shrq $32,  %rcx\\n\\t"
        
        
        "movq %rdi, %rsi\\n\\t"
        "shrq $32,  %rsi\\n\\t"
        
        "jmpq *%rax\\n\\t"
    #endif
);

当调用协程初次运行时,用 _co_arg_medium 函数向协程传递参数,此处参数传递兼容了 ucontext 的传参方式(4个32位整型参数)。

_co_arg_medium 是通过栈向协程传递参数的,所以在协程初次运行时将所需参数安置在栈中就可以了。只要理解当前编译器的函数调用约定,实现起来会挺容易的。

typedef struct asm_cctx_s 
    void **sp;
 cctx_s;

int 
co_start_asm(ci_s *ci)

    cctx_s *ctx = NULL;
    int ss = co_ssize(ci);
    char *stack = co_stack(ci);
    
    IF_EXPS_THEN_RETURN(!ci || !stack || !ss, 
        CODE_BADPARAM);

    ctx = co_cctx(ci);
    ctx->sp = (void **)(stack + ss);

    /* ctx->sp points to (void *),
       so arithmetic unit of 
       ctx->sp is sizeof(void *). 

       initial co-stack as follow: 
       ------+---------+------+----+----+----+
         ... |co_medium| co_fn| arg| ci |NULL|
       ------+---------+------+----+----+----+
       ^     ^                               ^
       |     |                               |
       stack sp                        stack+ss */
    *--ctx->sp = NULL;
    *--ctx->sp = ci;
    *--ctx->sp = co_arg(ci);
    *--ctx->sp = co_cofn(ci);
    *--ctx->sp = _co_arg_medium;

    /* Reserved for subroutines to 
       backup registers: ebp, ebx, 
       esi, edi on i386; rbp, rbx, 
       r12, r13, r14, r15 on amd64.*/
    ctx->sp -= CS_RESERVE_NR;
    
    (void)co_switch_asm(co_bcctx(ci), ctx);
    return CODE_NONE;

2.3 统一 asm 和 ucontext 对外接口

在用汇编指令和 ucontext 支撑协程切换时,此文对接口参数故意做兼容的目标是为了能对外提供统一的接口——通过预定义宏和一些少许包装就可以实现这个目标。

#ifndef _LN_CONTEXT_H_
#define _LN_CONTEXT_H_

#if defined(CCTX_ASM)
typedef struct asm_cctx_s 
    void **sp;
 cctx_s;

#if __i386
#define CS_RESERVE_NR (4)
/* curr and next passed value by eax adn edx respectively */
extern void __attribute__ ((__noinline__, __regparm__(2))) 
co_switch_asm(cctx_s *curr, cctx_s *next);


#elif __amd64
#define CS_RESERVE_NR (6)
extern void 
co_switch_asm(cctx_s *curr, cctx_s *next);
#endif

#include "ln_co.h"
extern int 
co_start_asm(ci_s *ci);
#define co_switch_to(curr, next) (co_switch_asm(curr, next); 0;)
#define co_start(ci) co_start_asm(ci);

#elif defined(CCTX_UC)
#include <ucontext.h>
typedef ucontext_t cctx_s;
#define co_switch_to(curr, next) swapcontext(curr, next)

#include "ln_co.h"
extern int 
co_start_uc(ci_s *ci);
#define co_start(ci) co_start_uc(ci)

#endif

#endif

love nature logic

3 co_send() 和 co_yield() 的实现

支撑协程上下文和切换的基石已经编写好了,现在开始实现具体的协程切换机制吧。先实现最基本的co_send() 和 co_yield()。

3.1 协程切换数据结构体

要实现协程切换的目标机制,需要什么样的结构体呢?这是一个演变的过程,此文几经调整才得到了以下数据结构体。

/**
 * used to record coroutine informations. */
typedef struct coroutine_info_s 
    void *co;  /* coroutine subroutine */
    void *arg; /* coroutine arguments */
    char *id;  /* coroutine id/name */
    int state; /* coroutine states */

    /* coroutine stack,
       current coroutine context. */
    char *stack;
    cctx_s cctx;

    /* the memory bears 
       coroutine's return-value */
    crv_s rv; 

    /* switch to the coroutine 
       corresponded by "back" when 
       current coroutine switching 
       or terminated. */
    ci_s *back;

    /* the cc_s current ci_s belongs to */
    cc_s *cc;
 ci_s;

/**
 * coroutine control unit structure */
typedef struct coroutine_control_s 
    ci_s *ci; /* point to the (ci_s) array */
    /* the unit/total, unused ci_s numbers. */
    int nr, unused;
    
    /* coroutine stack size(byte) in 
       current coroutine control unit.*/
    int ss;

    /* magic box for logic control */
    char box;

    /* next coroutine control unit */
    cc_s *next;
 cc_s;

此文将这个数据结构体定义在.c源文件中保护起来——不让其他文件访问其成员。

3.2 co_send()

co_send() 函数根据协程当前状态执行协程,分以下几种情况。
[1] 若当前协程为诞生(BORN)状态,则调用 _co_start() 调用协程运行,_co_start() 的本质是第2节实现的“协程初次调用函数”,该函数主要为协程传参并跳转协程处运行。协程调用 co_yield() 后将返回到 co_send() 调用 _co_start() 处继续运行。

[2] 若当前协程为挂起(SUSPENDING)状态,则调用 _co_switch() 恢复协程运行,_co_switch() 的本质是第2节实现的协程切换函数。协程调用 co_yield() 后将返回到 co_send() 调用 _co_switch() 处继续运行。

[3] 若当前协程为可运行(RUNNABLE)状态,说明协程未调用 co_yield() 属于自然返回(return),此时调用 _co_end() 结束协程运行。_co_end() 主要负责置标识协程已完成运行的状态。

void * 
co_send(ci_s *ci)

    int state;
    void *out = NULL;
    int   ret = CODE_NONE;

    IF_EXPS_THEN_RETURN(!ci, NULL);
    /* I just heard that CPU likes to predict backward jump.
       yield from mechanism should be more commonly used in 
       practical project, i guess. */
    IF_EXPS_THEN_GOTO_LABEL((BSTATE(ci) & BACKYF), _end);

    state = ci->state;
    char *id = ci->id ? ci->id : "unnamed_co";
    IF_EXPS_THEN_TIPS_AND_RETURN(
        (BORN > state) || (SUSPENDING < state), 
        NULL, "%s not running now\\n", id  );

    if (BORN == state) 
        ret = _co_start(ci);
     else if (SUSPENDING == state) 
        ret = _co_switch(ci);
    

    if (RUNNABLE == ci->state) 
        _co_end(ci);
    

    out = (PREGNANT != ci->state) ? &ci->rv : out;
    IF_EXPS_THEN_TIPS(ret, "co switch error: %d\\n", ret);

_end:
    return out;

3.3 co_yield()

co_yield() 主要用于协程切换,即从一个协程切换到另一个协程,并置协程管理的相关状态标识该协程已挂起。

int 
co_yield(ci_s *ci)

    int ret;
    IF_EXPS_THEN_RETURN(!ci, CODE_BADPARAM);
    ci->state = SUSPENDING;
    ret = co_switch_to(&ci->cctx, &BCCTX(ci));
    IF_EXPS_THEN_RETURN(ret, errno); 

    return ret;

另外,由于汇编指令描述的协程切换还不支持协程的 return 语句,所以此文用 co_end() 函数来明确结束协程已完成运行,此函数需在协程 return 语句之前调用(若有)。

co_end() 所做的事情无非是置位协程管理的状态为可运行状态并随之完成协程切换,以契合协程运行结束的条件。

void 
co_end(ci_s *ci)

    IF_EXPS_THEN_RETURN(!ci, VOIDV);
    ci->state |= RUNNABLE;
    (void)co_switch_to(&ci->cctx, &BCCTX(ci));

也可以修改汇编版的协程切换函数以支持协程的 return 语句,但此文还没有学会支持该机制的语法(快来指点^_^)。

4 co_yield_from() 的实现

如何实现 co_yield_from() 去同步基于 co_yield() 的协程呢?这个机制也是花了此文不少时间去思考,不过随着思考的积累而想明白后,对应的编码就不难啦—— co_yield_from() 同步协程返回后直接返回到 co_yield_from() 的父协程中即可。

void * 
co_yield_from(cc_s *cc, ci_s *self, 
    char *id, void *co, void *arg)

    int ret;
    ci_s *ci = NULL;
    void *t, *rv = NULL;
    
    cc->box |= YFCO;
    ci = co_co(cc, id, co, arg);
    cc->box &= ~YFCO;
    IF_EXPS_THEN_RETURN(!ci, NULL);

#define BSYF(ci) (ci->state && (BSTATE(ci) &= ~BACKYF))
    while (BSYF(ci) && (t = co_send(ci))) 
        if (BSTATE(ci)) BSTATE(ci) |= BACKYF;
        rv = t; self->rv = ci->rv;
        self->state = SUSPENDING;
        ret = co_switch_to(&self->cctx, &BCCTX(self));
        IF_EXPS_THEN_RETURN(ret, NULL);
    
#undef BSYF

    return rv;

此处 co_yield_from() 除了同步了基于 co_yield() 的协程外,还支持了边创建协程边运行的机制。从代码上看,该机制的支持仅有跟 YFCO,BACKYF 宏关联的几句代码,但他确实花了此文不少时间。

在真正实现该功能之前,此文差不多已经尝试了10多种皆未成功的逻辑控制方法呢。

#ifdef LOOP_AWHILE
    /* what a milestone 
       for running coroutine when creating new one!
       I have had try other dozens before this logic control. */
    _loop_awhile(cc, ci);
    giveup_sov(NAP);
#endif

5 协程并发调度的实现

此文按照以“单元”为单位管理多个协程,cc 管理一个单元,各单元上有 unit 个管理协程运行的数据结构体。各单元以链表的形式发生逻辑关联。

  | <--- ci unit ---> |  | <--- ci unit ---> |
  +------+-----+------+  +------+-----+------+
  | ci_s | ... | ci_s |  | ci_s | ... | ci_s | ...
  +------+-----+------+  +------+-----+------+
  ^                      ^
+-|--+-----+------+    +-|--+-----+------+
| ci | ... | next |--->| ci | ... | next |  ...
+----+-----+------+    +----+-----+------+
cc                     cc

协程并发运行除了边创造协程边运行的策略外,在调度环节对各个调度单元采取首尾间歇调度的方式调度协程运行,以提高时间效率。

| <--- ci unit ---> |
+------+------+-----+------+------+
| ci_s | ci_s | ... | ci_s | ci_s |
+------+------+-----+------+------+
^      ^            ^      ^
|      |            |      |
1      3            4      2
-------------> <-------------------

对应的代码如下

int 
co_loop(cc_s *cc)

    bool has_co;
    cc_s *p, *_cc = NULL;

    IF_EXPS_THEN_RETURN(!cc, CODE_BADPARAM);
_loop:
    has_co = false;
    for (_cc = cc; _cc; _cc = _cc->next) 
        if (CONR(_cc)) 
            has_co = true;
            _cc_cos_scheduler(_cc);
         else if (_cc != cc) 
            p->next = _cc->next;
            _put_cc(_cc);
            _cc = p;
        
        p = _cc;
    
IF_EXPS_THEN_GOTO_LABEL(has_co && giveup_sov(NAP), _loop);

    return CODE_NONE;


static void 
_cc_cos_scheduler(cc_s *cc)

    register int conr;
    register ci_s *s, *e;
    register cc_s *_cc = cc;

    s = _cc->ci;
    e = s + _cc->nr - 1;
    while ((conr = CONR(_cc)) && (s <= e)) 
        _running_ci(s); _running_ci(e);
        ++s; --e;
    
    
    return ;



static void inline 
_running_ci(ci_s *ci)

    if ((BORN <= ci->state) &&
        (SUSPENDING >= ci->state)) 
        co_send(ci);
    
    return ;

协程并发调度运行的本质就是开始实现的 co_send()。

7 一些提升

对于协程并发量较大时,程序中包含了一些可选的、有利于并发现象或内存资源节省的机制。

[1] 边创建边运行
边创建边运行协程除了可提升并发现象外,对于切换次数有限的协程来说,边创建边运行机制能节约内存——当切换次数有限的协程运行完毕后,后续协程可复用其内存资源。该机制特别适合切换次数较少的协程,可造就并发量无上限的可能(如 experiences 下的 _co_fn 的并发量就是无上限)。

[2] 内存预分配
在内存资源丰富的计算机上,可为所有协程预分配内存资源以免去内存申请和释放的开销。在内存资源较少的计算机上,可以内存资源复用的形式调度协程运行。

[3] 协程运行栈
经过一些测试,协程内部所需运行栈在11KiB左右。基于这个数值,用户协程可根据其协程函数所需栈空间(如查看协程对应的汇编指令)进行扩展。即同时运行 1000 个协程约需 11MB内存资源。

8 python yield,yield from 原理粗探

此文开篇提到,启发/激起此文完成此篇C程序的是 python 中的 yield, yield from 等机

以上是关于用汇编语言实现窗口的主要内容,如果未能解决你的问题,请参考以下文章

用汇编语言实现有颜色字符串的显示?

用汇编语言实现从键盘读一行信息,并存入用户定义的缓冲区buffer。如从键盘输入”myfriends”

一种在C语言中用汇编指令和 System V ucontext 支撑实现的协程切换

S5PV210开发板用汇编设置栈和调用C语言

S5PV210开发板用汇编设置栈和调用C语言

S5PV210开发板用汇编设置栈和调用C语言