2020.7.14

wine中的各类设计

说道设计我觉得刘奇师兄博士论文的图画得Win NT架构图和Wine架构图真的很有必要再次引用到这里,

左:Win NT的架构 | 右:Wine的架构

每看过一段时间的wine源码再来看这张图都有新的理解。

  • NT Kernel(左)对应Wine服务器(右),但wine服务器(wineserver)并不能单独起到NT kernel的作用还需要Wine可执行程序(wine)的支持。

2020.8.11新画的Win NT应用软件架构层次图,左为Linux用户程序的架构层次图,右为模拟出来的Windows用户程序的架构层次图,

wine_arch-formatted

wine启动过程

代码框架图:2020.7.13

WineStartUp-formatted

进程和内核(wineserver)通信

:这里进程都是指在wine的环境下运行的windows进程。这里的内核都是指windows的内核。

目前我看到的通信,即wineserver -d1能打印出来的,都是wine实现的windows的“系统调用”。

这些“系统调用”的函数指针被集中放在全局变量dlls/ntdll/unixlib.h: unix_funcs里,NtResumeThread

上面说的指针所指向的函数散落在dlls/ntdll/unix/的各个文件里,NtResumeThreaddlls/ntdll/unix/thread.c里;

这些“系统调用”由进程向wineserver请求,由wineserver实际执行,再有wineserver返回结果;这模拟了系统调用的过程:进程向内核发请求,内核执行,内核返回结果;

进程发送请求

2020.7.15

参考官方wineserver文档的Help to create a server request:

// @dlls/ntdll/unix/thread.c: NTSTATUS WINAPI NtResumeThread( HANDLE handle, ULONG *count )
SERVER_START_REQ( request_name )
{
    /* implicit variables: req, reply */
    req->input = 1;
    req->handle = wine_server_obj_handle( handle ); /* converts Windows HANDLE to a server obj_handle_t */
    if (wine_server_call( req ) != STATUS_SUCCESS)
    {
        /* SetLastError(), return error code, etc. */
    }
    do_something(reply->output);
}
SERVER_END_REQ;

其中变量req, reply的成员u是个包含了所有请求/返回类型的union,req.resume_thread_request, reply.resume_thread_reply

如此操作,进程向wineserver发送了一个执行resume_thread“系统调用”的请求。然后得到的返回结果在reply中。

wineserver接收请求&实际执行

wineserver的代码框架图::以resume_thread为例:

wineserver-formatted

可以见到resume_thread涉及到两个线程请求“系统调用”的线程被resume的线程

2021.04.22

trace

wineserver的trace属于集中式管理,和wine其他的trace属于分散式。trace输出函数位于,

  • call_req_handler
    • trace_request
    • trace_reply
2020.08.04

内存预留

内存预留由2步,由变量preload_info(wine_main_preload_info)做桥梁,

  1. preloader计算需要预留的空间
  2. 第二次启动wine时调用ntdll.so里__wine_main调用virtual_init函数完成内存预留
2020.08.05

分配空间如下,

reserve_memory-formatted

蓝色部分的内存预留是定死了的。

😀相较而言,比较有意思的是红色部分的内存预留是有很多空洞的,这些空洞来自于被分配给动态链接库已经占用了某些地址。也就是说,红色部分的内存预留并不是给固定地址的代码用的,是给地址不敏感的代码用的。😀因为要处理空洞,这个部分的预留算法也很有趣,利用mmap系统调用的MAP_FIXED标志,若成功表示没空洞;若失败表示有空洞,则二分采用递归重新尝试。这个算法对应dlls/ntdll/unix/virtual.c: 206~282 的代码。

2020.08.06

wine维护了一个已保留的内存地址链表dlls/ntdll/unix/virtual.c: 82: reserved_areas,各种用宏实现。

2021.04.23

physDev设备栈(图形设备GDI)

:参考Notes/Kernel/Windows/driver/logs.md 2021.01.12记录的WINE的physdev stack

这里的设备只指图形设备GDI。设备栈其实是设备驱动栈。

设备栈和面向对象的继承思想一致。设备<=>对象。设备在栈里的上下关系<=>对象的子父关系。

设备成员变量定义用grep查找\}.*_PDEVICE。都是PHYSDEV的“子类”。

设备函数函数申明用grep查找gdi_dc_funcs.*=。设备栈中存在函数继承和函数覆盖(override)现象。栈上层设备继承&覆盖下层设备的函数。GET_DC_PHYSDEV

push_dc_driver( PHYSDEV *dev, PHYSDEV physdev, const struct g di_dc_funcs *funcs )负责压栈。 用优先级(gdi_dc_funcs的priority变量)确定设备在栈里的顺序。 保证压入的设备一定位于栈顶,隐式的弹出超过当前优先级的设备。 没有显式弹栈的操作。

在添加优先级机制前的设备栈更容易理解。 压栈弹栈符合直觉,不含隐式操作。 优先级机制在2012.5.18的commit e94d3b4e0897d6c744595941bcfd724bdd47d3f9中添加。

打印机相关新增

优先级

  • psdrv, wingdidrv: GDI_PRIORITY_GRAPHICS_DRV=200,有pPutImage
  • GDI_PRIORITY_DIB_DRV=300
  • wingdidrv_compat: 308,无pPutImage

nullStrtchBlt

  • dlls/gdi32/painting.c: WINAPI Ellipse
    • pEllipse
      • dibdrv_Ellipse
      • nulldrv_Ellipse
2021.05.19

线程初始化

// dlls/ntdll/thread.c
thread_init(void)
{
    TEB *teb;
    // ...
    /// signal_前缀表示在该函数在模拟信号处理过程,
    /// 从该函数用到了stack_t ss数据结构看出,signal stack
    signal_alloc_thread(&teb);
    // ...
    signal_init_thread(teb)
    {
        /// 将teb
        wine_ldt_init_fs(thread->fs, &fs_entry);
    }
}