feat(desk): S3 底座 —— /dev/desk 共享内存通道 + 用户态协议模块
内核 devfs 新增 shm_device(/dev/desk):同一物理区间映射给所有打开者;main.vp 启动时 mm.malloc 出 DESK_SHM_SIZE(8MB) 并登记,分配失败只打日志跳过(explorer 的 open 会明确失败,不静默兜底)。内核只负责"分配 + 共享映射",区间内布局完全不参与。 vpsdk/desk.vp(新增协议模块):共享区固定布局(DeskHeader / WinSlot / 请求环 / 每窗口事件环 / 像素 arena)、单生产者单消费者环形队列(head/tail,跨进程无锁、全程指针直读写)、explorer 用的 desk_format/desk_arena_alloc 与 app 用的 desk_ready。请求/事件类型常量齐备(CREATE/DESTROY/MOVE/RESIZE/TITLE/FLUSH/ACTIVATE 与 CREATED/CLOSE/KEY/MOUSE/RESIZE)。 vpsdk/devfs.vp 增 DEV_DESK 与 shm_map();Explorer 启动即 open+mmap /dev/desk、desk_format 初始化并打印头信息,验证 IPC 底座可用。
This commit is contained in:
1 parent
6bd2390e08
commit
30708d3250
5 files changed
+317
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import vpsdk.devfs as devfs
|
||||
import vpsdk.desk as desk
|
||||
import vpsdk.process as process
|
||||
import vpsdk.syscall as syscall
|
||||
from stdint import *
|
||||
@@ -65,6 +66,26 @@ export def _start() -> int:
|
||||
viperlib.snprintf(&buf3, 96, "[explorer] input: kbd=%d mouse=%d\n", kfd, mfd)
|
||||
_dbg(&buf3)
|
||||
|
||||
# 5) 挂上 /dev/desk 共享区并格式化 —— explorer 是图形壳,共享区由它初始化,
|
||||
# app 侧只 desk_ready() 校验。这里先把头打出来证明 IPC 底座可用。
|
||||
desk_fd: int = devfs.desk_open()
|
||||
if desk_fd < 0:
|
||||
_dbg("[explorer] open /dev/desk FAILED\n")
|
||||
process.exit(1)
|
||||
return 0
|
||||
shm: uint64 = devfs.desk_map(desk_fd)
|
||||
if shm == 0:
|
||||
_dbg("[explorer] mmap /dev/desk FAILED\n")
|
||||
process.exit(1)
|
||||
return 0
|
||||
desk.desk_format(shm, info.width, info.height, info.pitch)
|
||||
hdr: desk.DeskHeader* = desk.DeskHeader*(shm + desk.desk_header_off())
|
||||
buf4: [144]char
|
||||
viperlib.snprintf(&buf4, 144,
|
||||
"[explorer] desk shm=0x%lx ready=%d slot=%lu arena=%u\n",
|
||||
shm, hdr.ready, uint64(desk.desk_slot_size()), hdr.arena_off)
|
||||
_dbg(&buf4)
|
||||
|
||||
# 图形壳暂时只是占位,常驻不退出(app 的 _start 绝不能再 return:栈顶下一格是 0)。
|
||||
while True:
|
||||
process.yield_cpu()
|
||||
|
||||
@@ -527,3 +527,52 @@ def devfs_register_fb0(fb: rawptr, width: int, height: int, pitch: int, format:
|
||||
fb0_file.fb_pitch = uint32(pitch)
|
||||
fb0_file.fb_format = uint32(format)
|
||||
return devfs_create_file("/dev/fb0", fb0_file, None)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# /dev/desk:图形壳 explorer 与各 app 之间的**共享内存通道**。
|
||||
#
|
||||
# 内核只做两件事:启动时分配一块物理内存、让每个 open 者 mmap 到**同一**物理区间。
|
||||
# 区间内部的布局(窗口表 / 各窗口像素缓冲 / 请求与事件环形队列)完全由用户态协议
|
||||
# 约定,见 vpsdk/desk.vp —— 内核不解析其内容,也不参与窗口语义。
|
||||
#
|
||||
# 为什么用 devfs 而不是新 syscall:syscall 表已对齐 Linux 且不留私有段;
|
||||
# "共享一块内存给多方"正好能用 设备 + mmap 表达(Linux 用 shm/memfd + MAP_SHARED,
|
||||
# 这里用 devfs 设备 + mmap 达到同样效果)。
|
||||
# ============================================================================
|
||||
# 共享区大小:vpsdk/desk.vp 里有一份同名同值的镜像(用户在 mmap 时必须先知道长度)。
|
||||
define DESK_SHM_SIZE = 8388608
|
||||
|
||||
virtual class shm_device(devfs_file):
|
||||
shm_phys: uint64
|
||||
shm_size: uint64
|
||||
|
||||
def open(self, path: const str, flags: int) -> int:
|
||||
return 0
|
||||
|
||||
def close(self, fd: int) -> int:
|
||||
return 0
|
||||
|
||||
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
|
||||
return -1
|
||||
|
||||
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
|
||||
return -1
|
||||
|
||||
def mmap_info(self, fd: int, out_phys: uint64*, out_size: uint64*) -> int:
|
||||
if out_phys: *out_phys = self.shm_phys
|
||||
if out_size: *out_size = self.shm_size
|
||||
return 0
|
||||
|
||||
desk_file: shm_device* = shm_device()
|
||||
|
||||
def devfs_register_desk(shm: rawptr, size: uint64) -> int:
|
||||
"""登记 /dev/desk。shm 是内核启动时 mm.malloc 出来的共享区;所有 open 者映射同一区间。"""
|
||||
global desk_file
|
||||
if not shm or size == 0:
|
||||
serial.puts("[devfs] desk shm register skipped (no memory)\n")
|
||||
return -1
|
||||
desk_file.__init__()
|
||||
desk_file.shm_phys = uint64(shm)
|
||||
desk_file.shm_size = size
|
||||
return devfs_create_file("/dev/desk", desk_file, None)
|
||||
@@ -298,6 +298,10 @@ class ViperKernel:
|
||||
# /dev/fb0:把 framebuffer 的物理区间与几何登记成设备节点,供用户态图形壳
|
||||
# explorer 经 open + ioctl(FB_GET_INFO) + mmap 接管屏幕绘制(S1)。
|
||||
devfs.devfs_register_fb0(fb, width, height, pitch, fb_format, fb_size)
|
||||
# /dev/desk:explorer 与各 app 的共享内存通道(S3)。分配失败就让登记跳过 ——
|
||||
# explorer 的 open 会明确失败并打日志,不做静默兜底。
|
||||
desk_shm: rawptr = mm.malloc(uint64(devfs.DESK_SHM_SIZE))
|
||||
devfs.devfs_register_desk(desk_shm, uint64(devfs.DESK_SHM_SIZE))
|
||||
|
||||
serial.puts("[main] sched started\n")
|
||||
|
||||
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
import vpsdk.devfs as devfs
|
||||
import vpsdk.fs as fs
|
||||
import string
|
||||
from stdint import *
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# /dev/desk 共享内存协议(S3):图形壳 explorer 与各 app 之间唯一的通信通道。
|
||||
#
|
||||
# 内核只提供"同一物理区间映射给所有打开者"(devfs 的 /dev/desk + mmap),
|
||||
# 区间内部的布局与语义全部由本模块约定 —— 用户态协商,内核不参与。
|
||||
#
|
||||
# 区间布局(偏移均相对共享区首,全部 8 字节对齐):
|
||||
# +0x000000 DeskHeader 全局头(含 magic/几何/窗口数/arena 指针)
|
||||
# +0x000100 [DESK_MAX_WIN]WinSlot 窗口槽表(explorer 是唯一写者)
|
||||
# +0x002000 RingCtrl + slots 请求环(app → explorer)
|
||||
# +0x004000 [DESK_MAX_WIN] RingCtrl+slots 每窗口事件环(explorer → app),每窗 0x1000
|
||||
# +0x200000 像素 arena 各窗口后备缓冲从这里切(bump 分配)
|
||||
#
|
||||
# 约定:
|
||||
# · explorer 启动后把 header.ready 置 1 —— app 见到 0 表示"图形壳还没起来";
|
||||
# · 窗口槽由 explorer 分配/回收;app 只通过请求环申请与更新,绝不直接改槽表;
|
||||
# · 像素缓冲由 app 直接写(arena 里属于自己窗口的那块),写完压一条 FLUSH 请求
|
||||
# 让 explorer 合成;这是"共享内存 + 消息"而不是"拷贝像素"的关键。
|
||||
# ============================================================================
|
||||
|
||||
# 必须与内核 VKernel/Kernel/drivers/devfs/devfs.vp 的 DESK_SHM_SIZE 一致
|
||||
# (mmap 时用户必须先给出长度,所以这里不能靠"问内核")。
|
||||
define DESK_SHM_SIZE = 8388608
|
||||
define DESK_MAGIC = 0x4B534544 # 'DESK'
|
||||
define DESK_VERSION = 1
|
||||
|
||||
define DESK_MAX_WIN = 8
|
||||
define DESK_TITLE_MAX = 32
|
||||
define DESK_RING_SLOTS = 64
|
||||
define DESK_MSG_SIZE = 40 # DeskMsg 的字节数(见下)
|
||||
|
||||
# 固定偏移
|
||||
define DESK_OFF_HEADER = 0x000000
|
||||
define DESK_OFF_SLOTS = 0x000100
|
||||
define DESK_OFF_REQ = 0x002000
|
||||
define DESK_OFF_EVENTS = 0x004000
|
||||
define DESK_OFF_ARENA = 0x200000
|
||||
|
||||
# 消息类型:请求(app → explorer)
|
||||
define DESK_REQ_CREATE = 1 # a=x b=y c=w d=h,title 在数据区;explorer 回 DESK_EVT_CREATED
|
||||
define DESK_REQ_DESTROY = 2
|
||||
define DESK_REQ_MOVE = 3 # a=x b=y
|
||||
define DESK_REQ_RESIZE = 4 # a=w b=h
|
||||
define DESK_REQ_TITLE = 5 # title 在数据区
|
||||
define DESK_REQ_FLUSH = 6 # 像素已写,请求合成该窗口
|
||||
define DESK_REQ_ACTIVATE = 7 # 置顶并聚焦
|
||||
# 消息类型:事件(explorer → app)
|
||||
define DESK_EVT_CREATED = 101 # a=win_id(失败为 -1)
|
||||
define DESK_EVT_CLOSE = 102 # 用户点了关闭按钮,app 应当退出
|
||||
define DESK_EVT_KEY = 103 # a=ascii b=keycode c=state
|
||||
define DESK_EVT_MOUSE = 104 # a=x b=y c=buttons d=type
|
||||
define DESK_EVT_RESIZE = 105 # a=w b=h
|
||||
|
||||
|
||||
@__attribute__(packed)
|
||||
class DeskHeader:
|
||||
magic: uint32
|
||||
version: uint32
|
||||
ready: int # explorer 置 1 表示图形壳已接管
|
||||
win_count: int
|
||||
fb_w: uint32
|
||||
fb_h: uint32
|
||||
fb_pitch: uint32
|
||||
_pad0: uint32
|
||||
arena_off: uint32
|
||||
arena_used: uint32
|
||||
_pad1: uint32
|
||||
|
||||
|
||||
@__attribute__(packed)
|
||||
class WinSlot:
|
||||
used: int
|
||||
pid: int
|
||||
active: int
|
||||
minimized: int
|
||||
z_order: int
|
||||
ux: int
|
||||
uy: int
|
||||
w: int
|
||||
h: int
|
||||
style: int
|
||||
cursor: int
|
||||
fb_off: uint32
|
||||
fb_w: uint32
|
||||
fb_h: uint32
|
||||
_pad2: uint32
|
||||
title: [DESK_TITLE_MAX]char
|
||||
|
||||
|
||||
@__attribute__(packed)
|
||||
class DeskMsg:
|
||||
type: uint32
|
||||
win: int
|
||||
a: int
|
||||
b: int
|
||||
c: int
|
||||
d: int
|
||||
_pad: uint32
|
||||
|
||||
|
||||
@__attribute__(packed)
|
||||
class RingCtrl:
|
||||
head: uint32 # 消费者下一次取的位置
|
||||
tail: uint32 # 生产者下一次放的位置
|
||||
size: uint32 # 槽数
|
||||
_pad: uint32
|
||||
|
||||
|
||||
def desk_slot_size() -> uint32:
|
||||
return uint32(WinSlot.__sizeof__())
|
||||
|
||||
def desk_ring_ctrl_size() -> uint32:
|
||||
return uint32(RingCtrl.__sizeof__())
|
||||
|
||||
def desk_msg_size() -> uint32:
|
||||
return uint32(DeskMsg.__sizeof__())
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 环形队列:控制块在给定偏移处,槽紧随其后。单生产者单消费者,无锁 ——
|
||||
# head == tail 空,(tail+1)%size == head 满。跨进程共享内存下靠"每次读写都落到
|
||||
# 内存"的顺序保证:这里不缓存跨调用的状态,全部经指针读写。
|
||||
# ---------------------------------------------------------------------------
|
||||
def ring_init(ctrl_off: uint64, size: uint32):
|
||||
c: RingCtrl* = RingCtrl*(ctrl_off)
|
||||
c.head = uint32(0)
|
||||
c.tail = uint32(0)
|
||||
c.size = size
|
||||
c._pad = uint32(0)
|
||||
for i in range(size):
|
||||
m: DeskMsg* = DeskMsg*(ctrl_off + uint64(desk_ring_ctrl_size()) + uint64(i) * uint64(desk_msg_size()))
|
||||
string.memset(m, 0, desk_msg_size())
|
||||
|
||||
def ring_push(ctrl_off: uint64, msg: DeskMsg) -> int:
|
||||
"""生产者入队。返回 0 = 成功,-1 = 满。"""
|
||||
c: RingCtrl* = RingCtrl*(ctrl_off)
|
||||
nxt: uint32 = (c.tail + uint32(1)) % c.size
|
||||
if nxt == c.head:
|
||||
return -1
|
||||
slot: DeskMsg* = DeskMsg*(ctrl_off + uint64(desk_ring_ctrl_size()) + uint64(c.tail) * uint64(desk_msg_size()))
|
||||
slot.type = msg.type
|
||||
slot.win = msg.win
|
||||
slot.a = msg.a
|
||||
slot.b = msg.b
|
||||
slot.c = msg.c
|
||||
slot.d = msg.d
|
||||
c.tail = nxt # 最后写 tail:读者以 tail 为"有货"的标志
|
||||
return 0
|
||||
|
||||
def ring_pop(ctrl_off: uint64, out: DeskMsg*) -> int:
|
||||
"""消费者出队。返回 1 = 取到一条,0 = 空。"""
|
||||
c: RingCtrl* = RingCtrl*(ctrl_off)
|
||||
if c.head == c.tail:
|
||||
return 0
|
||||
slot: DeskMsg* = DeskMsg*(ctrl_off + uint64(desk_ring_ctrl_size()) + uint64(c.head) * uint64(desk_msg_size()))
|
||||
out.type = slot.type
|
||||
out.win = slot.win
|
||||
out.a = slot.a
|
||||
out.b = slot.b
|
||||
out.c = slot.c
|
||||
out.d = slot.d
|
||||
c.head = (c.head + uint32(1)) % c.size
|
||||
return 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 各固定结构的共享区偏移
|
||||
# ---------------------------------------------------------------------------
|
||||
def desk_header_off() -> uint64:
|
||||
return uint64(DESK_OFF_HEADER)
|
||||
|
||||
def desk_slot_off(win_id: int) -> uint64:
|
||||
return uint64(DESK_OFF_SLOTS) + uint64(win_id) * uint64(desk_slot_size())
|
||||
|
||||
def desk_req_ring_off() -> uint64:
|
||||
return uint64(DESK_OFF_REQ)
|
||||
|
||||
def desk_evt_ring_off(win_id: int) -> uint64:
|
||||
return uint64(DESK_OFF_EVENTS) + uint64(win_id) * uint64(0x1000)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 挂载:open /dev/desk + mmap 整个共享区,返回区首线性地址(0 = 失败)。
|
||||
# explorer 负责格式化(magic/version/几何/各环 init/ready=1);app 只校验。
|
||||
# ---------------------------------------------------------------------------
|
||||
def desk_open() -> int:
|
||||
return fs.open(devfs.DEV_DESK, uint32(0))
|
||||
|
||||
def desk_map(fd: int) -> uint64:
|
||||
return devfs.shm_map(fd, uint64(DESK_SHM_SIZE))
|
||||
|
||||
def desk_format(base: uint64, fb_w: uint32, fb_h: uint32, fb_pitch: uint32):
|
||||
"""explorer 专用:初始化共享区头与各环。"""
|
||||
h: DeskHeader* = DeskHeader*(base + desk_header_off())
|
||||
string.memset(h, 0, DeskHeader.__sizeof__())
|
||||
h.magic = uint32(DESK_MAGIC)
|
||||
h.version = uint32(DESK_VERSION)
|
||||
h.fb_w = fb_w
|
||||
h.fb_h = fb_h
|
||||
h.fb_pitch = fb_pitch
|
||||
h.arena_off = uint32(DESK_OFF_ARENA)
|
||||
h.arena_used = uint32(0)
|
||||
h.win_count = 0
|
||||
h.ready = 0
|
||||
ring_init(base + desk_req_ring_off(), uint32(DESK_RING_SLOTS))
|
||||
for i in range(DESK_MAX_WIN):
|
||||
ring_init(base + desk_evt_ring_off(i), uint32(DESK_RING_SLOTS))
|
||||
# 区首清零后 magic 必须最后写,避免 app 读到半初始化状态
|
||||
h.magic = uint32(DESK_MAGIC)
|
||||
h.ready = 1
|
||||
|
||||
def desk_ready(base: uint64) -> int:
|
||||
"""app 专用:判断图形壳是否已接管并可通信。"""
|
||||
h: DeskHeader* = DeskHeader*(base + desk_header_off())
|
||||
if h.magic != uint32(DESK_MAGIC):
|
||||
return 0
|
||||
return h.ready
|
||||
|
||||
def desk_arena_alloc(base: uint64, size: uint32) -> uint32:
|
||||
"""explorer 专用:从像素 arena 切一块,返回相对区首的偏移(0 = 空间不足)。"""
|
||||
h: DeskHeader* = DeskHeader*(base + desk_header_off())
|
||||
need: uint32 = (size + uint32(15)) & ~uint32(15)
|
||||
used: uint32 = h.arena_used
|
||||
if uint32(DESK_OFF_ARENA) + used + need > uint32(DESK_SHM_SIZE):
|
||||
return uint32(0)
|
||||
h.arena_used = used + need
|
||||
return uint32(DESK_OFF_ARENA) + used
|
||||
@@ -17,6 +17,7 @@ define DEV_SERIAL1 = "/dev/serial1"
|
||||
define DEV_FB0 = "/dev/fb0"
|
||||
define DEV_KBD = "/dev/input/keyboard"
|
||||
define DEV_MOUSE = "/dev/input/mouse"
|
||||
define DEV_DESK = "/dev/desk"
|
||||
|
||||
# ioctl 命令与返回结构:必须与 VKernel/Kernel/drivers/devfs/devfs.vp 的
|
||||
# FB_GET_INFO / FbInfo 逐字段一致(packed;4×u32 + 2×u64 = 32 字节)。
|
||||
@@ -55,3 +56,12 @@ def kbd_open() -> int:
|
||||
|
||||
def mouse_open() -> int:
|
||||
return fs.open(DEV_MOUSE, uint32(0))
|
||||
|
||||
def shm_map(fd: int, size: uint64) -> uint64:
|
||||
"""把共享内存设备映射进本进程,返回线性起始地址(0 = 失败)。
|
||||
|
||||
与 fb_map 同一机制(设备 mmap_info 给出的物理区间);不同点在于 /dev/desk
|
||||
的区间对**所有**打开者都是同一块物理内存,因此天然可跨进程共享。
|
||||
"""
|
||||
return syscall.mmap(0, size, uint64(syscall.PROT_READ | syscall.PROT_WRITE),
|
||||
uint64(syscall.MAP_SHARED), uint64(fd), 0)
|
||||
Reference in new issue
Block a user