现在探讨几个核心概念:进程、PCB(进程控制块)、应用程序、窗口嵌入以及它们之间的逻辑和物理关系,还有内核机制。我将逐一详细解释。
进程与PCB的物理逻辑关系
应用程序如何通过PCB组织进程
进程与窗口嵌入的逻辑关系
这些关系的内核机制
实例说明
- 进程与PCB的物理逻辑关系
逻辑关系:
进程是程序的一次执行实例,是操作系统进行资源分配和调度的基本单位。
PCB(进程控制块)是操作系统为了管理进程而设置的一个数据结构,用于描述进程的基本情况和活动过程,是进程存在的唯一标志。
逻辑上,每个进程都有一个对应的PCB。操作系统通过PCB来感知进程的存在,并根据PCB中的信息对进程进行控制和管理。
物理关系:
在物理内存中,操作系统会为每个PCB分配一块内存空间(通常在内核空间)。这些PCB通常以链表或数组的形式组织起来,方便操作系统进行遍历和查找。
例如,在Linux内核中,PCB对应的是task_struct结构体,所有进程的task_struct通过链表连接(实际上是一个双向循环链表,但现代内核中已更复杂,例如使用红黑树等)。
- 应用程序如何通过PCB组织进程
应用程序本身是一个静态的概念,它存储在磁盘上。当我们运行一个应用程序时,操作系统会创建一个进程,并为其分配一个PCB。应用程序的代码和数据被加载到进程的地址空间中。
一个应用程序可以对应多个进程(例如,多个浏览器窗口),每个进程有自己独立的PCB。同样,一个进程也可以对应多个应用程序(虽然不常见,但在某些复杂系统中可能存在)。
操作系统通过PCB来组织进程,例如:
进程状态:在PCB中记录进程当前状态(就绪、运行、阻塞等),从而进行调度。
程序计数器:记录进程下一条要执行的指令的地址。
内存管理信息:记录进程的页表、段表等,用于地址转换。
文件描述符表:记录进程打开的文件。
其他资源信息:如打开的设备、信号处理函数等。
通过PCB,操作系统可以管理应用程序运行时的所有信息。
- 进程与窗口嵌入的逻辑关系
在图形用户界面(GUI)系统中,窗口通常与进程相关联。一个进程可以创建一个或多个窗口,每个窗口通常对应一个线程(UI线程)。窗口嵌入是指将一个进程创建的窗口嵌入到另一个进程的窗口中。
逻辑关系:
每个窗口通常属于一个特定的进程(更准确地说,属于创建该窗口的线程)。
窗口嵌入涉及两个进程:宿主进程(其窗口包含另一个窗口)和被嵌入进程(其窗口被嵌入)。
窗口嵌入的本质是修改窗口的父子关系,即设置被嵌入窗口的父窗口为宿主进程中的某个窗口。
这样,从用户的角度看,两个窗口似乎属于同一个应用程序,但实际上它们可能属于不同的进程。
- 这些关系的内核机制
进程与PCB的内核机制:
内核中维护着所有进程的PCB,并通过调度器根据PCB中的状态信息来切换进程。当创建一个新进程时,内核会分配一个PCB并初始化;当进程终止时,内核会回收PCB。
窗口嵌入的内核机制:
窗口嵌入通常由窗口管理器(Window Manager)和图形子系统(如Windows的Win32子系统,Linux的X Window或Wayland)处理。内核可能不直接处理窗口嵌入,但提供进程间通信(IPC)和窗口句柄管理机制。
在Windows中,窗口嵌入通过SetParentAPI实现,该函数会修改窗口的父子关系,并将子窗口的显示区域限制在父窗口的客户区内。这涉及到用户模式下的窗口管理器和内核模式下的窗口驱动程序(如win32k.sys)的协作。
- 实例说明
让我们以Windows操作系统为例,详细说明一个应用程序(如记事本)的进程创建、PCB(在Windows中称为EPROCESS)以及窗口嵌入的过程。
实例:记事本进程的创建和窗口嵌入
进程创建:
当我们双击记事本程序(notepad.exe)时,Windows资源管理器会调用CreateProcess函数。该函数会:
在内核中创建一个新的进程对象(EPROCESS)和线程对象(ETHREAD)。
为进程分配地址空间,加载notepad.exe的代码和数据。
创建进程的主线程,并开始执行。
PCB(EPROCESS):
Windows内核中的EPROCESS结构体包含了进程的所有关键信息,例如:
进程ID(PID)
父进程ID
进程环境块(PEB)的地址
内存管理信息(页表、工作集等)
句柄表(指向进程打开的对象)
进程状态等。
内核通过一个全局的进程链表来管理所有进程的EPROCESS结构。
窗口创建:
记事本进程的主线程会创建一个窗口,这是通过调用CreateWindowEx等API实现的。窗口创建过程中,窗口管理器会分配一个窗口对象(在Windows内核中,窗口对象由win32k.sys管理),并返回一个句柄(HWND)。这个窗口对象与创建它的线程(进而与进程)关联。
窗口嵌入:
假设我们想将记事本窗口嵌入到另一个应用程序(我们称为宿主应用程序)的窗口中。宿主应用程序会调用SetParent函数,传入记事本窗口的句柄和宿主窗口中某个子窗口的句柄(或宿主窗口本身)作为父窗口。
SetParent函数会修改记事本窗口的父窗口,使其成为宿主窗口的子窗口。这样,记事本窗口的显示就会限制在宿主窗口的客户区内,并且随着宿主窗口移动而移动。
内核机制:
当SetParent被调用时,用户模式的窗口管理器(user32.dll)会通过系统调用(如NtUserSetParent)将请求发送到内核模式的窗口驱动程序(win32k.sys)。
win32k.sys会更新窗口对象的父窗口字段,并重新计算窗口的位置和可见区域。
由于窗口属于不同的进程,这里还涉及到进程间通信(IPC)和跨进程的窗口消息传递。Windows的窗口消息机制允许跨进程发送消息,但需要特殊的处理(例如,使用SendMessage或PostMessage)。
注意事项:
窗口嵌入后,被嵌入的窗口(记事本)仍然运行在自己的进程中,宿主进程无法直接访问记事本进程的内存或资源,除非通过IPC机制。
窗口嵌入可能会带来安全问题,因为被嵌入的窗口可能接收来自宿主窗口的输入消息,也可能尝试执行一些操作(如绘制)在宿主窗口的上下文中。
通过这个例子,我们可以看到进程、PCB(EPROCESS)、窗口和窗口嵌入之间的关系,以及操作系统内核和窗口管理系统是如何协作来实现这些功能的。
总结
进程是动态执行的实体,PCB是操作系统用于管理进程的数据结构。
应用程序通过进程执行,进程通过PCB组织。
窗口嵌入允许不同进程的窗口在视觉上合并,但其底层仍然是独立的进程,通过窗口管理器和内核协作实现。
理解这些关系对于开发复杂的多进程应用程序(如集成其他应用程序的界面)或进行系统级编程非常重要。
进程、PCB与窗口嵌入的深度内核解析
我将从操作系统内核的视角,深入探讨进程、PCB、应用程序和窗口嵌入之间的物理逻辑关系、组织机制和内核实现原理。
一、进程与PCB的物理逻辑关系
1. 物理内存中的PCB组织
Linux内核中的task_struct物理布局:
// Linux 5.x内核的task_struct部分定义
struct task_struct {
// 1. 状态和标识(约64字节)
volatile long state; // 进程状态
void *stack; // 内核栈指针
unsigned int flags; // 进程标志
int prio; // 静态优先级
int static_prio; // 静态优先级
int normal_prio; // 正常优先级
unsigned int policy; // 调度策略
pid_t pid; // 进程ID
pid_t tgid; // 线程组ID
// 2. 进程关系(约64字节)
struct task_struct __rcu *parent; // 父进程
struct list_head children; // 子进程链表
struct list_head sibling; // 兄弟进程链表
struct task_struct *group_leader; // 线程组领导
// 3. 内存管理(约256字节)
struct mm_struct *mm; // 内存描述符
struct mm_struct *active_mm; // 活动内存描述符
// 4. 文件系统(约128字节)
struct fs_struct *fs; // 文件系统信息
struct files_struct *files; // 打开文件表
// 5. 信号处理(约128字节)
struct signal_struct *signal; // 信号处理
struct sighand_struct *sighand; // 信号处理函数
sigset_t blocked; // 阻塞信号集
sigset_t real_blocked;
// 6. 调度相关(约96字节)
struct sched_entity se; // 调度实体
struct sched_rt_entity rt; // 实时调度实体
struct sched_dl_entity dl; // 截止时间调度实体
// 7. 时间统计(约64字节)
u64 utime; // 用户态运行时间
u64 stime; // 内核态运行时间
unsigned long nvcsw; // 自愿上下文切换计数
unsigned long nivcsw; // 非自愿上下文切换计数
// 8. 其他(约128字节)
struct thread_struct thread; // 架构特定线程信息
struct css_set __rcu *cgroups; // cgroup设置
struct list_head tasks; // 全局进程链表节点
struct list_head ptrace_entry; // ptrace链表节点
// 总大小:约1000-2000字节,取决于内核配置
} __randomize_layout;
物理内存中的PCB组织方式:
内核地址空间高地址
┌─────────────────────────────────┐
│ 内核代码和数据 │
├─────────────────────────────────┤
│ task_struct数组(slab分配) │
│ ┌─────────────────────┐ │
│ │ task_struct[0] │ │ ← 每个CPU核心的per-CPU变量
│ │ - pid=1 (init) │ │
│ │ - state=RUNNING │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ task_struct[1] │ │
│ │ - pid=100 (bash) │ │
│ │ - state=INTERRUPTIBLE │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ task_struct[2] │ │
│ │ - pid=200 (chrome) │ │
│ │ - state=RUNNING │ │
│ └─────────────────────┘ │
│ ... │
├─────────────────────────────────┤
│ 内核栈(每个进程8KB) │
│ ┌─────────────────────┐ │
│ │ 进程1内核栈 │ ← thread_info在栈底 │
│ │ ┌───────────────┐ │ │
│ │ │thread_info │ │ │
│ │ │task*指针指向 │ │ │
│ │ │task_struct │ │ │
│ │ └───────────────┘ │ │
│ │ 栈增长方向 ↑ │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ 进程2内核栈 │ │
│ └─────────────────────┘ │
│ ... │
├─────────────────────────────────┤
│ 进程描述符缓存 │
│ (task_struct_cache slab) │
├─────────────────────────────────┤
│ 进程链表头 │
│ init_task (pid=0) │
│ ┌─────────────────────┐ │
│ │ tasks链表头 │ │
│ │ prev→下一个PCB │ │
│ │ next→下一个PCB │ │
│ └─────────────────────┘ │
├─────────────────────────────────┤
│ 就绪队列(rq) │
│ struct rq per_cpu变量 │
└─────────────────────────────────┘
内核地址空间低地址
PCB物理内存分配机制:
// Linux内核源码:kernel/fork.c
// 进程创建时的PCB分配
static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
{
struct task_struct *tsk;
struct thread_info *ti;
// 1. 从task_struct_cache slab缓存中分配PCB内存
tsk = alloc_task_struct_node(node);
if (!tsk)
return NULL;
// 2. 分配内核栈(通常8KB或16KB)
ti = alloc_thread_info_node(tsk, node);
if (!ti)
goto free_tsk;
// 3. 设置thread_info与task_struct的关联
// thread_info位于内核栈底部
ti->task = tsk;
tsk->stack = ti;
// 4. 复制父进程的task_struct内容
err = arch_dup_task_struct(tsk, orig);
if (err)
goto free_ti;
// 5. 设置栈顶指针(架构相关)
setup_thread_stack(tsk, orig);
return tsk;
free_ti:
free_thread_info(ti);
free_tsk:
free_task_struct(tsk);
return NULL;
}
// PCB在内核slab缓存中的分配
struct kmem_cache *task_struct_cachep;
void __init fork_init(void)
{
// 创建task_struct专用的slab缓存
task_struct_cachep = kmem_cache_create("task_struct",
sizeof(struct task_struct), ARCH_MIN_TASKALIGN,
SLAB_PANIC | SLAB_ACCOUNT, NULL);
}
2. PCB与进程的物理映射关系
内核中进程查找机制:
// 通过PID查找PCB的机制
struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns)
{
struct pid *pid;
// 1. 通过PID哈希表查找pid结构
pid = find_pid_ns(nr, ns);
if (!pid)
return NULL;
// 2. 从pid结构获取task_struct
return pid_task(pid, PIDTYPE_PID);
}
// PID到PCB的映射数据结构
struct pid
{
atomic_t count; // 引用计数
unsigned int level; // 命名空间层级
struct hlist_node tasks[PIDTYPE_MAX]; // 任务链表
struct rcu_head rcu;
struct upid numbers[1]; // 不同命名空间中的PID
};
struct upid {
int nr; // 当前命名空间的PID
struct pid_namespace *ns; // 所属命名空间
struct hlist_node pid_chain; // PID哈希表节点
};
// PID哈希表(全局)
static struct hlist_head *pid_hash;
进程树物理组织:
// 内核中的进程树维护
void __init proc_caches_init(void)
{
// 初始化各种进程相关的缓存
sighand_cachep = kmem_cache_create("sighand_cache",
sizeof(struct sighand_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
sighand_ctor);
signal_cachep = kmem_cache_create("signal_cache",
sizeof(struct signal_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
NULL);
}
// 进程创建时设置父子关系
long _do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr,
unsigned long tls)
{
struct task_struct *p;
int trace = 0;
long nr;
// 创建新的PCB
p = copy_process(clone_flags, stack_start, stack_size,
parent_tidptr, child_tidptr, tls, trace);
if (IS_ERR(p))
return PTR_ERR(p);
// 获取新进程的PID
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid);
// 如果是CLONE_PARENT,设置相同的父进程
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
// 唤醒新进程
wake_up_new_task(p);
return nr;
}
二、应用程序如何通过PCB组织进程
1. 应用程序加载与进程创建的完整流程
用户空间 内核空间
┌─────────────────┐ ┌─────────────────────┐
│ 应用程序 │ │ 内核 │
│ (executable) │ │ │
└────────┬────────┘ └──────────┬──────────┘
│ execve()系统调用 │
├─────────────────────────────────────►
│ │
│ 1. 查找可执行文件 │
│ nameidata │
│ │
│ 2. 检查权限和能力 │
│ security_bprm_check│
│ │
│ 3. 设置二进制格式 │
│ search_binary_handler│
│ │
│ 4. 加载程序到内存 │
│ load_elf_binary │
│ │
│ 5. 设置参数和环境变量 │
│ setup_arg_pages │
│ │
│ 6. 开始执行 │
│ start_thread │
│◄─────────────────────────────────────┤
│ │
┌────────▼────────┐ ┌───────────▼──────────┐
│ 新进程运行 │ │ 更新PCB │
│ main()函数 │ │ - 内存映射 │
│ │ │ - 文件描述符 │
│ │ │ - 信号处理 │
└─────────────────┘ └─────────────────────┘
2. 应用程序内存空间在PCB中的组织
// mm_struct内存描述符结构
struct mm_struct {
struct vm_area_struct *mmap; // 虚拟内存区域链表
struct rb_root mm_rb; // 虚拟内存区域红黑树
u32 vmacache_seqnum; // VM缓存序列号
unsigned long mmap_base; // mmap起始地址
unsigned long task_size; // 任务虚拟内存大小
unsigned long start_code, end_code; // 代码段起止
unsigned long start_data, end_data; // 数据段起止
unsigned long start_brk, brk; // 堆起止
unsigned long start_stack; // 栈起始
unsigned long arg_start, arg_end; // 命令行参数
unsigned long env_start, env_end; // 环境变量
// 页表管理
pgd_t *pgd; // 页全局目录
atomic_t mm_users; // 使用计数
atomic_t mm_count; // 引用计数
int map_count; // VMA数量
struct list_head mmlist; // 所有mm_struct链表
spinlock_t page_table_lock; // 页表锁
// 反向映射
struct rmap_wait_anon_vma *anon_vma_chain;
struct rmap_wait_anon_vma *anon_vma;
// 内存策略
struct mempolicy *mempolicy;
// 内存统计
unsigned long total_vm; // 总虚拟内存
unsigned long locked_vm; // 锁定内存
unsigned long pinned_vm; // 固定内存
unsigned long data_vm; // 数据段内存
unsigned long exec_vm; // 可执行内存
unsigned long stack_vm; // 栈内存
};
// 应用程序加载时设置内存映射
static int load_elf_binary(struct linux_binprm *bprm)
{
struct file *interpreter = NULL;
struct elf_phdr *elf_ppnt, *elf_phdata;
struct elf_phdr *interp_elf_phdata = NULL;
unsigned long error;
unsigned long elf_bss, elf_brk;
int retval, i;
// 1. 解析ELF头部
loc->elf_ex = *((struct elfhdr *)bprm->buf);
// 2. 读取程序头表
elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
// 3. 遍历程序段
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
elf_ppnt = elf_phdata + i;
if (elf_ppnt->p_type == PT_LOAD) {
// 加载段到内存
error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
elf_prot, elf_flags, 0);
// 更新内存统计
if (!error) {
unsigned long size = elf_ppnt->p_memsz;
unsigned long end = vaddr + size;
if (end > elf_brk)
elf_brk = end;
if ((elf_ppnt->p_flags & PF_X) && end > elf_bss)
elf_bss = end;
}
}
}
// 4. 设置堆和栈
current->mm->start_brk = current->mm->brk = elf_brk;
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
// 5. 设置参数和环境变量
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
executable_stack);
// 6. 设置程序入口点
start_thread(regs, elf_entry, bprm->p);
return 0;
}
3. 应用程序资源在PCB中的组织
// files_struct文件描述符表
struct files_struct {
atomic_t count; // 引用计数
struct fdtable __rcu *fdt; // 文件描述符表
struct fdtable fdtab; // 内联文件描述符表
spinlock_t file_lock; // 文件锁
unsigned int next_fd; // 下一个可用的fd
unsigned long close_on_exec_init[1]; // exec时关闭的文件
unsigned long open_fds_init[1]; // 打开文件位图
struct file __rcu * fd_array[NR_OPEN_DEFAULT]; // 文件指针数组
};
// 应用程序打开文件时更新PCB
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct file *file;
char *tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
// 分配文件描述符
fd = get_unused_fd_flags(flags);
if (fd >= 0) {
// 打开文件
file = do_filp_open(dfd, tmp, &op);
if (IS_ERR(file)) {
put_unused_fd(fd);
fd = PTR_ERR(file);
} else {
// 将文件与fd关联
fsnotify_open(file);
fd_install(fd, file);
// 更新PCB中的files_struct
struct files_struct *files = current->files;
__set_open_fd(fd, files);
}
}
putname(tmp);
return fd;
}
// 信号处理结构在PCB中的组织
struct signal_struct {
atomic_t sigcnt; // 信号计数
atomic_t live; // 活动线程数
int nr_threads; // 线程数
struct list_head thread_head; // 线程链表
wait_queue_head_t wait_chldexit; // 等待子进程退出
struct task_struct *curr_target; // 当前目标进程
struct sigpending shared_pending; // 共享待处理信号
// 信号处理函数
struct sigaction action[_NSIG]; // 信号动作
sigset_t blocked; // 阻塞信号集
sigset_t real_blocked;
// 资源限制
struct rlimit rlim[RLIM_NLIMITS]; // 资源限制
unsigned long flags;
// 进程组和会话
pid_t pgrp; // 进程组ID
pid_t session; // 会话ID
};
三、进程与窗口嵌入的逻辑关系
1. 窗口系统架构与进程关系
┌─────────────────────────────────────────────────────┐
│ 用户空间窗口系统 │
├─────────────────────────────────────────────────────┤
│ 应用程序A 应用程序B │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 窗口1 │ │ 窗口2 │ │
│ │ HWND=1001 │ │ HWND=1002 │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ │ 窗口消息 │ 窗口消息 │
│ │ (WM_PAINT, WM_MOUSEMOVE)│ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 窗口管理器(如X11/Wayland) │ │
│ │ 管理窗口位置、Z序、焦点、合成等 │ │
│ └─────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ 内核空间 │
├─────────────────────────────────────────────────────┤
│ 进程A task_struct 进程B task_struct │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ mm_struct │ │ mm_struct │ │
│ │ files_struct│ │ files_struct│ │
│ │ signal_struct │ signal_struct │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ │ 系统调用接口 │ 系统调用接口 │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 显示驱动程序 │ │
│ │ DirectFB/DRI/DRM/KMS │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 显卡硬件 │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
2. 窗口句柄(HWND)与进程的内核映射
Windows内核中的窗口对象:
// Windows内核中的窗口对象(win32k.sys)
typedef struct _WINDOW_OBJECT
{
HANDLE hWnd; // 窗口句柄
PVOID pWnd; // 指向WND结构的指针
// 窗口属性
DWORD dwStyle; // 窗口样式
DWORD dwExStyle; // 扩展样式
RECT rcWindow; // 窗口矩形
RECT rcClient; // 客户区矩形
// 窗口关系
struct _WINDOW_OBJECT *parent; // 父窗口
struct _WINDOW_OBJECT *owner; // 所有者窗口
struct _WINDOW_OBJECT *child; // 第一个子窗口
struct _WINDOW_OBJECT *sibling; // 下一个兄弟窗口
// 进程关联
PEPROCESS Process; // 所属进程的EPROCESS
PETHREAD Thread; // 创建窗口的线程
// 窗口类
PCLSOBJ pcls; // 窗口类对象
// 消息队列
PQMSG pqmsg; // 消息队列
// 安全描述符
PSECURITY_DESCRIPTOR pSecurityDescriptor;
// 其他属性
ULONG_PTR dwUserData; // 用户数据
ATOM atomClass; // 窗口类原子
UINT cbWndExtra; // 额外窗口数据大小
} WINDOW_OBJECT, *PWINDOW_OBJECT;
// 窗口句柄表(Handle Table)
typedef struct _HANDLE_TABLE
{
ULONG_PTR TableCode; // 句柄表编码
PEPROCESS QuotaProcess; // 配额进程
PEPROCESS UniqueProcessId; // 进程ID
EX_PUSH_LOCK HandleLock; // 句柄表锁
// 句柄表项
struct _HANDLE_TABLE_ENTRY *HandleTable;
} HANDLE_TABLE, *PHANDLE_TABLE;
窗口嵌入的内核机制:
// Windows中SetParent的系统调用实现
NTSTATUS NtUserSetParent(
HWND hWndChild,
HWND hWndNewParent)
{
PWINDOW_OBJECT pWndChild, pWndNewParent;
PEPROCESS CurrentProcess;
// 1. 获取当前进程
CurrentProcess = PsGetCurrentProcess();
// 2. 验证窗口句柄并获取窗口对象
pWndChild = ValidateHwnd(hWndChild);
if (!pWndChild)
return STATUS_INVALID_HANDLE;
// 3. 检查权限:窗口必须属于当前进程或具有足够权限
if (pWndChild->Process != CurrentProcess) {
if (!SeSinglePrivilegeCheck(SeDebugPrivilege, CurrentProcess)) {
return STATUS_ACCESS_DENIED;
}
}
// 4. 获取新父窗口对象(如果指定)
if (hWndNewParent) {
pWndNewParent = ValidateHwnd(hWndNewParent);
if (!pWndNewParent)
return STATUS_INVALID_HANDLE;
// 检查循环父子关系
if (TestCyclicParent(pWndChild, pWndNewParent)) {
return STATUS_INVALID_PARAMETER;
}
} else {
pWndNewParent = NULL;
}
// 5. 获取窗口对象锁
LockWindowObject(pWndChild);
// 6. 更新窗口父子关系
if (pWndChild->parent != pWndNewParent) {
// 从原父窗口的子链表中移除
if (pWndChild->parent) {
RemoveFromChildList(pWndChild->parent, pWndChild);
}
// 设置新父窗口
pWndChild->parent = pWndNewParent;
// 添加到新父窗口的子链表
if (pWndNewParent) {
AddToChildList(pWndNewParent, pWndChild);
// 更新窗口样式:变为子窗口
pWndChild->dwStyle |= WS_CHILD;
pWndChild->dwStyle &= ~WS_POPUP;
// 重新计算窗口位置(相对坐标)
ConvertToParentCoordinates(pWndChild, pWndNewParent);
} else {
// 没有父窗口,恢复为顶级窗口
pWndChild->dwStyle &= ~WS_CHILD;
pWndChild->dwStyle |= WS_POPUP;
}
// 7. 触发窗口重新绘制和位置调整
SendMessage(hWndChild, WM_PARENTCHANGED, 0, 0);
SetWindowPos(hWndChild, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
// 8. 释放锁
UnlockWindowObject(pWndChild);
return STATUS_SUCCESS;
}
// 消息转发的内核机制
NTSTATUS NtUserSendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam)
{
PWINDOW_OBJECT pWnd;
PETHREAD TargetThread;
PMSGQUEUE TargetQueue;
// 1. 验证窗口句柄
pWnd = ValidateHwnd(hWnd);
if (!pWnd)
return STATUS_INVALID_HANDLE;
// 2. 获取目标窗口的线程
TargetThread = pWnd->Thread;
// 3. 获取目标线程的消息队列
TargetQueue = GetThreadMessageQueue(TargetThread);
// 4. 检查是否跨进程发送消息
if (TargetThread != PsGetCurrentThread()) {
// 跨进程消息发送需要特殊处理
// a. 验证参数是否可以跨进程访问
if (!ValidateMessageParameters(Msg, wParam, lParam, TRUE)) {
return STATUS_ACCESS_VIOLATION;
}
// b. 复制消息数据到目标进程地址空间
PMSG pMsgCopy = CopyMessageToTargetProcess(
TargetThread->Process,
Msg, wParam, lParam);
if (!pMsgCopy) {
return STATUS_NO_MEMORY;
}
// c. 将消息放入目标线程的消息队列
InsertMessage(TargetQueue, pMsgCopy);
// d. 唤醒目标线程(如果需要)
if (TargetThread->State == Waiting) {
WakeThread(TargetThread);
}
} else {
// 同一进程内发送消息
PMSG pMsg = AllocateMessage(Msg, wParam, lParam);
InsertMessage(TargetQueue, pMsg);
}
return STATUS_SUCCESS;
}
3. X Window系统(Linux)的窗口嵌入机制
// X Server中的窗口数据结构
typedef struct _Window {
XID id; // 窗口ID
Drawable drawable; // 可绘制对象
// 窗口属性
int x, y; // 位置
unsigned int width, height; // 尺寸
unsigned int border_width; // 边框宽度
// 窗口关系
struct _Window *parent; // 父窗口
struct _Window *firstChild; // 第一个子窗口
struct _Window *lastChild; // 最后一个子窗口
struct _Window *nextSibling; // 下一个兄弟窗口
struct _Window *prevSibling; // 上一个兄弟窗口
// 客户端关联
ClientPtr client; // 客户端(进程)
// 窗口类
Mask eventMask; // 事件掩码
// 可视性
Bool viewable; // 是否可见
Bool mapped; // 是否已映射
// 其他属性
Colormap colormap; // 颜色映射
Cursor cursor; // 光标
} WindowRec, *WindowPtr;
// XReparentWindow请求处理
int ProcReparentWindow(ClientPtr client)
{
WindowPtr pWin, pNewParent;
REQUEST(xReparentWindowReq);
// 1. 查找窗口对象
pWin = LookupWindow(stuff->window, client);
if (!pWin) {
client->errorValue = stuff->window;
return BadWindow;
}
// 2. 查找新父窗口
if (stuff->parent) {
pNewParent = LookupWindow(stuff->parent, client);
if (!pNewParent) {
client->errorValue = stuff->parent;
return BadWindow;
}
// 检查循环父子关系
if (IsDescendant(pNewParent, pWin)) {
return BadMatch;
}
} else {
pNewParent = client->defaultRootWindow;
}
// 3. 验证权限
if (!CheckAccess(pWin, client, DixReparentAccess) ||
!CheckAccess(pNewParent, client, DixAddAccess)) {
return BadAccess;
}
// 4. 执行重设父窗口操作
return ReparentWindow(pWin, pNewParent,
stuff->x, stuff->y, TRUE);
}
// 实际的重设父窗口函数
int ReparentWindow(WindowPtr pWin, WindowPtr pNewParent,
int x, int y, Bool sendEvent)
{
WindowPtr pOldParent = pWin->parent;
// 1. 从原父窗口的子链表中移除
if (pOldParent) {
if (pWin == pOldParent->firstChild) {
pOldParent->firstChild = pWin->nextSibling;
}
if (pWin == pOldParent->lastChild) {
pOldParent->lastChild = pWin->prevSibling;
}
if (pWin->prevSibling) {
pWin->prevSibling->nextSibling = pWin->nextSibling;
}
if (pWin->nextSibling) {
pWin->nextSibling->prevSibling = pWin->prevSibling;
}
}
// 2. 添加到新父窗口的子链表
pWin->parent = pNewParent;
if (pNewParent) {
if (!pNewParent->firstChild) {
pNewParent->firstChild = pNewParent->lastChild = pWin;
pWin->prevSibling = pWin->nextSibling = NULL;
} else {
pWin->prevSibling = pNewParent->lastChild;
pWin->nextSibling = NULL;
pNewParent->lastChild->nextSibling = pWin;
pNewParent->lastChild = pWin;
}
// 3. 更新窗口位置(转换为新父窗口的坐标系)
pWin->x = x;
pWin->y = y;
// 4. 重新计算绝对坐标
ComputeAbsoluteCoordinates(pWin);
}
// 5. 发送重设父窗口事件
if (sendEvent) {
SendReparentNotify(pWin, pOldParent, pNewParent, x, y);
}
// 6. 重新计算可视区域
RegionPtr pOldVisible = pWin->valdata->before.borderVisible;
RegionPtr pNewVisible = NotViewable(pWin) ? NullRegion :
BorderVisible(pWin);
if (REGION_NOT_EQUAL(pOldVisible, pNewVisible)) {
WindowExposures(pWin, pOldVisible, pNewVisible);
}
return Success;
}
四、完整实例:跨进程窗口嵌入与消息路由
1. 完整的Windows跨进程窗口嵌入示例
// CrossProcessWindowEmbedder.cpp
#include <windows.h>
#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <memory>
// 共享内存结构
#pragma pack(push, 1)
struct SharedMemoryData {
DWORD hostProcessId;
DWORD embeddedProcessId;
HWND hostWindow;
HWND embeddedWindow;
bool ready;
char windowTitle[256];
};
#pragma pack(pop)
class CrossProcessEmbedder {
private:
HANDLE hMapFile;
SharedMemoryData* pSharedData;
HWND hHostWindow;
HWND hEmbeddedWindow;
DWORD dwEmbeddedProcessId;
std::atomic<bool> running{false};
// 消息钩子
HHOOK hMouseHook;
HHOOK hKeyboardHook;
// 消息转发线程
std::thread messageThread;
public:
CrossProcessEmbedder() : hMapFile(NULL), pSharedData(NULL),
hHostWindow(NULL), hEmbeddedWindow(NULL),
hMouseHook(NULL), hKeyboardHook(NULL) {}
~CrossProcessEmbedder() {
cleanup();
}
// 宿主进程:创建共享内存和窗口
bool InitializeAsHost(const std::wstring& windowTitle) {
// 1. 创建共享内存
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
sizeof(SharedMemoryData),
L"Global\\CrossProcessEmbedder"
);
if (!hMapFile) {
std::cerr << "CreateFileMapping failed: " << GetLastError() << std::endl;
return false;
}
// 2. 映射共享内存
pSharedData = (SharedMemoryData*)MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0, 0, sizeof(SharedMemoryData)
);
if (!pSharedData) {
std::cerr << "MapViewOfFile failed: " << GetLastError() << std::endl;
CloseHandle(hMapFile);
return false;
}
// 3. 初始化共享数据
memset(pSharedData, 0, sizeof(SharedMemoryData));
pSharedData->hostProcessId = GetCurrentProcessId();
pSharedData->ready = false;
wcscpy_s((wchar_t*)pSharedData->windowTitle,
sizeof(pSharedData->windowTitle) / sizeof(wchar_t),
windowTitle.c_str());
// 4. 创建宿主窗口
if (!CreateHostWindow(windowTitle)) {
UnmapViewOfFile(pSharedData);
CloseHandle(hMapFile);
return false;
}
// 5. 设置全局钩子(用于捕获嵌入窗口的输入)
InstallHooks();
// 6. 启动消息转发线程
running = true;
messageThread = std::thread(&CrossProcessEmbedder::MessagePumpThread, this);
return true;
}
// 被嵌入进程:连接到共享内存
bool InitializeAsEmbedded() {
// 1. 打开已存在的共享内存
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE,
L"Global\\CrossProcessEmbedder"
);
if (!hMapFile) {
std::cerr << "OpenFileMapping failed: " << GetLastError() << std::endl;
return false;
}
// 2. 映射共享内存
pSharedData = (SharedMemoryData*)MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0, 0, sizeof(SharedMemoryData)
);
if (!pSharedData) {
std::cerr << "MapViewOfFile failed: " << GetLastError() << std::endl;
CloseHandle(hMapFile);
return false;
}
// 3. 等待宿主进程准备好
while (!pSharedData->ready) {
Sleep(100);
}
// 4. 查找自己的主窗口
hEmbeddedWindow = FindWindow(NULL, (LPCWSTR)pSharedData->windowTitle);
if (!hEmbeddedWindow) {
std::cerr << "Cannot find embedded window" << std::endl;
UnmapViewOfFile(pSharedData);
CloseHandle(hMapFile);
return false;
}
// 5. 将窗口嵌入到宿主窗口中
return EmbedWindow();
}
private:
// 创建宿主窗口
bool CreateHostWindow(const std::wstring& title) {
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = HostWindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = L"CrossProcessHostWindow";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
std::cerr << "RegisterClassEx failed: " << GetLastError() << std::endl;
return false;
}
hHostWindow = CreateWindowEx(
WS_EX_CLIENTEDGE,
L"CrossProcessHostWindow",
title.c_str(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL,
NULL,
GetModuleHandle(NULL),
this
);
if (!hHostWindow) {
std::cerr << "CreateWindowEx failed: " << GetLastError() << std::endl;
return false;
}
// 更新共享内存
pSharedData->hostWindow = hHostWindow;
ShowWindow(hHostWindow, SW_SHOW);
UpdateWindow(hHostWindow);
return true;
}
// 宿主窗口过程
static LRESULT CALLBACK HostWindowProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
CrossProcessEmbedder* pThis = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (CrossProcessEmbedder*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
} else {
pThis = (CrossProcessEmbedder*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis) {
return pThis->HandleHostMessage(hwnd, msg, wParam, lParam);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT HandleHostMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
// 创建嵌入区域
CreateWindow(L"STATIC", L"",
WS_CHILD | WS_VISIBLE | SS_SUNKEN,
10, 10, 600, 400,
hwnd, (HMENU)1001,
GetModuleHandle(NULL), NULL);
// 通知被嵌入进程可以开始了
pSharedData->ready = true;
break;
}
case WM_SIZE: {
// 调整嵌入窗口大小
if (hEmbeddedWindow && IsWindow(hEmbeddedWindow)) {
RECT rc;
GetClientRect(hwnd, &rc);
// 留出边距
rc.left += 10;
rc.top += 10;
rc.right -= 20;
rc.bottom -= 20;
SetWindowPos(hEmbeddedWindow, NULL,
rc.left, rc.top,
rc.right - rc.left, rc.bottom - rc.top,
SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
}
case WM_DESTROY:
running = false;
if (messageThread.joinable()) {
messageThread.join();
}
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// 安装全局钩子
void InstallHooks() {
// 鼠标钩子
hMouseHook = SetWindowsHookEx(
WH_MOUSE_LL,
MouseHookProc,
GetModuleHandle(NULL),
0
);
// 键盘钩子
hKeyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookProc,
GetModuleHandle(NULL),
0
);
}
// 鼠标钩子过程(静态)
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
MSLLHOOKSTRUCT* pMouse = (MSLLHOOKSTRUCT*)lParam;
// 获取宿主进程实例
CrossProcessEmbedder* pThis = GetInstanceFromSharedMemory();
if (pThis && pThis->hEmbeddedWindow) {
// 检查鼠标是否在嵌入窗口上
POINT pt = pMouse->pt;
RECT rcEmbedded;
GetWindowRect(pThis->hEmbeddedWindow, &rcEmbedded);
if (PtInRect(&rcEmbedded, pt)) {
// 将屏幕坐标转换为嵌入窗口的客户区坐标
ScreenToClient(pThis->hEmbeddedWindow, &pt);
// 转发鼠标消息到嵌入窗口
switch (wParam) {
case WM_MOUSEMOVE:
PostMessage(pThis->hEmbeddedWindow, WM_MOUSEMOVE,
0, MAKELPARAM(pt.x, pt.y));
break;
case WM_LBUTTONDOWN:
PostMessage(pThis->hEmbeddedWindow, WM_LBUTTONDOWN,
MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
break;
case WM_LBUTTONUP:
PostMessage(pThis->hEmbeddedWindow, WM_LBUTTONUP,
0, MAKELPARAM(pt.x, pt.y));
break;
}
// 如果需要阻止消息继续传递,可以返回非零值
// return 1;
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// 键盘钩子过程
static LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
KBDLLHOOKSTRUCT* pKeyboard = (KBDLLHOOKSTRUCT*)lParam;
CrossProcessEmbedder* pThis = GetInstanceFromSharedMemory();
if (pThis && pThis->hEmbeddedWindow) {
// 检查嵌入窗口是否有焦点
HWND hForeground = GetForegroundWindow();
if (hForeground == pThis->hHostWindow) {
// 转发键盘消息到嵌入窗口
PostMessage(pThis->hEmbeddedWindow,
wParam == WM_KEYDOWN ? WM_KEYDOWN : WM_KEYUP,
pKeyboard->vkCode,
0);
// 阻止宿主窗口处理这些按键
// return 1;
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// 从共享内存获取实例指针
static CrossProcessEmbedder* GetInstanceFromSharedMemory() {
// 在实际应用中,这需要更复杂的设计
// 这里简化处理
return nullptr;
}
// 嵌入窗口
bool EmbedWindow() {
if (!hEmbeddedWindow || !pSharedData->hostWindow) {
return false;
}
// 1. 获取宿主窗口的嵌入区域
HWND hEmbedArea = GetDlgItem(pSharedData->hostWindow, 1001);
if (!hEmbedArea) {
return false;
}
// 2. 设置窗口样式(移除边框等)
LONG_PTR style = GetWindowLongPtr(hEmbeddedWindow, GWL_STYLE);
style = style & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX |
WS_MAXIMIZEBOX | WS_SYSMENU);
style = style | WS_CHILD;
SetWindowLongPtr(hEmbeddedWindow, GWL_STYLE, style);
// 3. 设置父窗口
SetParent(hEmbeddedWindow, hEmbedArea);
// 4. 调整窗口大小
RECT rc;
GetClientRect(hEmbedArea, &rc);
SetWindowPos(hEmbeddedWindow, NULL,
0, 0, rc.right, rc.bottom,
SWP_NOZORDER | SWP_NOACTIVATE);
// 5. 更新共享内存
pSharedData->embeddedWindow = hEmbeddedWindow;
pSharedData->embeddedProcessId = GetCurrentProcessId();
// 6. 显示窗口
ShowWindow(hEmbeddedWindow, SW_SHOW);
return true;
}
// 消息泵线程(转发消息)
void MessagePumpThread() {
while (running) {
// 检查是否有嵌入窗口
if (hEmbeddedWindow && IsWindow(hEmbeddedWindow)) {
// 转发宿主窗口的某些消息到嵌入窗口
// 这里可以添加特定的消息转发逻辑
}
Sleep(10); // 避免CPU占用过高
}
}
// 清理资源
void cleanup() {
// 移除钩子
if (hMouseHook) {
UnhookWindowsHookEx(hMouseHook);
hMouseHook = NULL;
}
if (hKeyboardHook) {
UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = NULL;
}
// 关闭共享内存
if (pSharedData) {
UnmapViewOfFile(pSharedData);
pSharedData = NULL;
}
if (hMapFile) {
CloseHandle(hMapFile);
hMapFile = NULL;
}
}
};
// 宿主进程主函数
int WINAPI HostMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
CrossProcessEmbedder embedder;
if (!embedder.InitializeAsHost(L"Cross-Process Window Host")) {
std::cerr << "Failed to initialize as host" << std::endl;
return 1;
}
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
// 被嵌入进程主函数
int WINAPI EmbeddedMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
// 先创建自己的窗口
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"EmbeddedWindowClass";
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(
0, L"EmbeddedWindowClass", L"Embedded Application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
// 然后连接到宿主进程
CrossProcessEmbedder embedder;
if (embedder.InitializeAsEmbedded()) {
std::cout << "Successfully embedded window" << std::endl;
}
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
2. Linux X11跨进程窗口嵌入示例
// xembed.c - X11跨进程窗口嵌入
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// 共享内存(使用XSHM扩展)
#include <sys/ipc.h>
#include <sys/shm.h>
// 定义嵌入协议原子
#define _XEMBED_INFO "XEMBED_INFO"
#define _XEMBED_MAPPED "XEMBED_MAPPED"
// XEmbed消息类型
#define XEMBED_EMBEDDED_NOTIFY 0
#define XEMBED_WINDOW_ACTIVATE 1
#define XEMBED_WINDOW_DEACTIVATE 2
#define XEMBED_REQUEST_FOCUS 3
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
#define XEMBED_FOCUS_NEXT 6
#define XEMBED_FOCUS_PREV 7
#define XEMBED_MODALITY_ON 10
#define XEMBED_MODALITY_OFF 11
// 宿主进程
int host_main(Display* display, Window root) {
// 创建宿主窗口
Window host = XCreateSimpleWindow(display, root,
100, 100, 800, 600,
1, BlackPixel(display, 0),
WhitePixel(display, 0));
XSelectInput(display, host, ExposureMask | KeyPressMask);
XMapWindow(display, host);
// 创建嵌入容器窗口
Window container = XCreateSimpleWindow(display, host,
10, 10, 600, 400,
1, BlackPixel(display, 0),
WhitePixel(display, 0));
XMapWindow(display, container);
// 创建共享内存区域用于通信
int shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);
if (shmid < 0) {
perror("shmget");
return 1;
}
// 将共享内存ID写入属性,供被嵌入进程读取
Atom shm_atom = XInternAtom(display, "_XEMBED_SHM_ID", False);
XChangeProperty(display, host, shm_atom,
XA_INTEGER, 32, PropModeReplace,
(unsigned char*)&shmid, 1);
printf("Host window ID: 0x%lx\n", host);
printf("Container window ID: 0x%lx\n", container);
printf("Shared memory ID: %d\n", shmid);
printf("Waiting for embedded window...\n");
// 等待被嵌入进程连接
XEvent event;
while (1) {
XNextEvent(display, &event);
if (event.type == ClientMessage) {
// 检查是否是嵌入通知
Atom xembed_info = XInternAtom(display, "_XEMBED_INFO", False);
if (event.xclient.message_type == xembed_info) {
printf("Received XEmbed notification\n");
// 获取被嵌入窗口ID
Window embedded = event.xclient.data.l[2];
printf("Embedded window ID: 0x%lx\n", embedded);
// 重新设置父窗口
XReparentWindow(display, embedded, container, 0, 0);
// 映射窗口
XMapWindow(display, embedded);
// 发送XEmbed映射通知
XEvent xembed_event;
memset(&xembed_event, 0, sizeof(xembed_event));
xembed_event.type = ClientMessage;
xembed_event.xclient.window = embedded;
xembed_event.xclient.message_type =
XInternAtom(display, "_XEMBED_MAPPED", False);
xembed_event.xclient.format = 32;
xembed_event.xclient.data.l[0] = CurrentTime;
xembed_event.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY;
xembed_event.xclient.data.l[2] = 0; // 版本
xembed_event.xclient.data.l[3] = container;
xembed_event.xclient.data.l[4] = 0;
XSendEvent(display, embedded, False, 0, &xembed_event);
XFlush(display);
printf("Window embedded successfully\n");
}
} else if (event.type == KeyPress) {
if (event.xkey.keycode == 9) { // Escape键
break;
}
}
}
// 清理
shmctl(shmid, IPC_RMID, NULL);
XCloseDisplay(display);
return 0;
}
// 被嵌入进程
int embedded_main(Display* display, Window root, Window host_window) {
// 创建被嵌入窗口
Window embedded = XCreateSimpleWindow(display, root,
0, 0, 500, 300,
1, BlackPixel(display, 0),
WhitePixel(display, 0));
XSelectInput(display, embedded, ExposureMask | KeyPressMask);
// 设置XEmbed支持
Atom xembed_info = XInternAtom(display, "_XEMBED_INFO", False);
long xembed_data[2] = {0, 0}; // 版本和标志
XChangeProperty(display, embedded, xembed_info,
XA_INTEGER, 32, PropModeReplace,
(unsigned char*)xembed_data, 2);
// 发送嵌入请求到宿主窗口
XEvent event;
memset(&event, 0, sizeof(event));
event.type = ClientMessage;
event.xclient.window = host_window;
event.xclient.message_type = XInternAtom(display, "_XEMBED", False);
event.xclient.format = 32;
event.xclient.data.l[0] = CurrentTime;
event.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY;
event.xclient.data.l[2] = 0;
event.xclient.data.l[3] = embedded;
event.xclient.data.l[4] = 0;
XSendEvent(display, host_window, False, 0, &event);
XFlush(display);
printf("Embedded window ID: 0x%lx\n", embedded);
printf("Sent embed request to host window 0x%lx\n", host_window);
// 等待被嵌入
while (1) {
XNextEvent(display, &event);
if (event.type == MapNotify) {
printf("Window mapped\n");
} else if (event.type == ReparentNotify) {
printf("Reparented to container window\n");
} else if (event.type == ClientMessage) {
Atom xembed_mapped = XInternAtom(display, "_XEMBED_MAPPED", False);
if (event.xclient.message_type == xembed_mapped) {
printf("Received XEmbed mapping notification\n");
break;
}
} else if (event.type == KeyPress) {
printf("Key pressed in embedded window\n");
if (event.xkey.keycode == 9) { // Escape键
break;
}
}
}
XCloseDisplay(display);
return 0;
}
int main(int argc, char* argv[]) {
Display* display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "Cannot open display\n");
return 1;
}
Window root = DefaultRootWindow(display);
if (argc > 1 && strcmp(argv[1], "--embedded") == 0) {
if (argc < 3) {
fprintf(stderr, "Usage: %s --embedded <host_window_id>\n", argv[0]);
return 1;
}
Window host_window = strtoul(argv[2], NULL, 0);
return embedded_main(display, root, host_window);
} else {
return host_main(display, root);
}
}
五、内核机制总结
1. PCB与进程的核心映射机制
| 组件 | 物理存储 | 内核数据结构 | 作用 |
|---|---|---|---|
| PCB | 内核空间slab缓存 | task_struct (Linux) / EPROCESS (Windows) | 进程控制块,存储进程所有信息 |
| 内存映射 | 进程地址空间 | mm_struct (Linux) / VAD树 (Windows) | 管理进程的虚拟内存布局 |
| 文件描述符 | 进程私有表 | files_struct (Linux) / HANDLE_TABLE (Windows) | 管理打开的文件和资源句柄 |
| 信号处理 | PCB内部 | signal_struct (Linux) / KPROCESS信号部分 (Windows) | 处理进程信号和异常 |
| 调度信息 | PCB内部 | sched_entity (Linux) / KTHREAD调度部分 (Windows) | 进程调度和优先级管理 |
2. 窗口嵌入的内核数据流
应用程序A(宿主) 内核 应用程序B(被嵌入)
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 窗口消息 │ │ │ │ 窗口消息 │
│ (WM_xxx) │ │ │ │ (WM_xxx) │
│ │ │ │ │ │
│ 1. SetParent() │─────►│ 1. 验证窗口句柄 │◄────┤ 窗口创建 │
│ │ │ 2. 检查权限 │ │ │
│ │ │ 3. 更新窗口关系 │ │ │
│ │ │ │ │ │
│ 2. 输入事件 │─────►│ 4. 路由消息 │─────►│ 5. 处理事件 │
│ (鼠标/键盘) │ │ a. 查找目标进程 │ │ │
│ │ │ b. 复制消息数据 │ │ │
│ 3. 接收绘制 │◄────┤ c. 发送到消息队列│◄────┤ 6. 绘制输出 │
│ (WM_PAINT) │ │ │ │ (GDI操作) │
│ │ │ │ │ │
│ 4. 窗口尺寸调整 │─────►│ 7. 更新窗口位置 │─────►│ 8. 重新绘制 │
│ (WM_SIZE) │ │ 和可见区域 │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
3. 关键内核机制的对比
| 机制 | Linux/X11实现 | Windows实现 | 核心原理 |
|---|---|---|---|
| 进程标识 | PID + 命名空间 | PID + 会话 + 桌面 | 唯一标识进程 |
| 窗口标识 | XID (Window ID) | HWND (窗口句柄) | 窗口的唯一标识 |
| 窗口关系 | 父窗口指针链表 | 父子窗口句柄表 | 树状结构管理 |
| 消息传递 | X协议消息 | Windows消息系统 | 事件驱动通信 |
| 权限检查 | X权限机制 + SELinux | 安全描述符 + 访问令牌 | 基于权限的访问控制 |
| 内存共享 | XSHM扩展 | 共享内存 + 内存映射文件 | 跨进程数据共享 |
| 图形渲染 | DRI/DRM + OpenGL | GDI/DirectX + DWM | 图形子系统合成 |
4. 实际应用中的内核交互
// Linux内核中进程与窗口的交互追踪
// 可以使用ftrace或perf追踪系统调用
// 追踪窗口创建的系统调用
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/enable
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/enable
// 查看进程的窗口相关信息
$ cat /proc/<pid>/maps # 查看进程内存映射
$ ls -la /proc/<pid>/fd/ # 查看打开的文件描述符
$ cat /proc/<pid>/status # 查看进程状态
$ ps -eLf | grep <pid> # 查看进程的线程
// Windows内核调试
// 使用WinDbg查看进程和窗口信息
!process 0 0 # 列出所有进程
.process /r /p <EPROCESS> # 切换到指定进程
!handle # 查看句柄表
!object \Windows\WindowStations # 查看窗口站对象
六、现代演进:Wayland与微内核架构
1. Wayland的进程窗口管理
// Wayland合成器中的窗口管理
struct weston_surface {
struct wl_resource *resource; // Wayland资源
struct weston_view *view; // 视图
// 缓冲区管理
struct wl_buffer *buffer; // 图形缓冲区
struct wl_shm_buffer *shm_buffer; // 共享内存缓冲区
// 位置和变换
struct weston_geometry geometry; // 几何信息
struct weston_transform transform; // 变换矩阵
// 输入处理
struct weston_pointer *pointer; // 指针输入
struct weston_touch *touch; // 触摸输入
struct weston_keyboard *keyboard; // 键盘输入
// 输出关联
struct weston_output *output; // 显示输出
// 客户端关联
struct weston_client *client; // 客户端(进程)
};
// Wayland中的窗口嵌入(通过subsurface)
struct weston_subsurface {
struct wl_resource *resource; // subsurface资源
struct weston_surface *parent; // 父表面
struct weston_surface *surface; // 子表面
// 位置和同步
int32_t x, y; // 相对位置
enum wl_subcompositor_sync_mode sync_mode; // 同步模式
// 链表管理
struct wl_list parent_link; // 父表面的子表面链表
struct wl_list surface_link; // 表面的subsurface链表
};
// 创建subsurface(窗口嵌入)
static void subsurface_create(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource,
struct wl_resource *parent_resource) {
struct weston_surface *surface = wl_resource_get_user_data(surface_resource);
struct weston_surface *parent = wl_resource_get_user_data(parent_resource);
// 创建subsurface
struct weston_subsurface *sub = calloc(1, sizeof *sub);
if (!sub) {
wl_resource_post_no_memory(resource);
return;
}
sub->surface = surface;
sub->parent = parent;
sub->x = sub->y = 0;
sub->sync_mode = WL_SUBCOMPOSITOR_SYNC_MODE_DESYNC;
// 建立关联
wl_list_insert(&parent->subsurface_list, &sub->parent_link);
wl_list_insert(&surface->subsurface_list, &sub->surface_link);
// 重新计算合成
weston_surface_damage(parent);
}
2. 微内核架构下的进程通信
// L4微内核中的进程间通信
struct L4_Thread {
L4_Word_t id; // 线程ID
L4_Word_t space; // 地址空间ID
// IPC端点
L4_Word_t ipc_ep; // IPC端点
// 消息寄存器
L4_MsgTag_t mr[L4_MR_MAX]; // 消息寄存器
// 调度信息
L4_Word_t priority; // 优先级
L4_Word_t time; // 时间片
// 等待队列
struct list_head wait_queue; // 等待队列
};
// L4 IPC消息传递
L4_MsgTag_t L4_Ipc(L4_ThreadId_t to,
L4_ThreadId_t from,
L4_Word_t timeouts,
L4_ThreadId_t *partner) {
// 1. 构建消息
L4_Msg_t msg;
L4_Set_MsgTag(&msg, tag);
// 2. 执行IPC系统调用
asm volatile (
"movl %1, %%eax\n" // 系统调用号
"movl %2, %%ebx\n" // 目标线程ID
"movl %3, %%ecx\n" // 发送者线程ID
"movl %4, %%edx\n" // 超时设置
"int $0x30\n" // 微内核调用
"movl %%eax, %0\n" // 结果
: "=r"(result)
: "r"(L4_IPC_SYSCALL), "r"(to.raw), "r"(from.raw), "r"(timeouts)
: "%eax", "%ebx", "%ecx", "%edx"
);
// 3. 解析结果
if (partner) {
*partner = L4_GlobalId(result >> 16, result & 0xFFFF);
}
return L4_ErrorCode();
}
总结
核心要点总结:
-
PCB是进程在物理内存中的代表:
- 存储在内核空间的slab缓存中
- 通过PID哈希表快速查找
- 包含进程的所有状态和资源信息
-
应用程序通过PCB组织进程资源:
- 内存映射(mm_struct)管理地址空间
- 文件描述符表管理打开的资源
- 信号处理结构处理异步事件
- 调度实体管理CPU时间分配
-
窗口嵌入本质是修改窗口的父子关系:
- 窗口句柄(HWND/XID)标识窗口
- 窗口对象存储在内核或窗口服务器中
- SetParent/XReparentWindow修改窗口树结构
- 消息路由需要跨进程通信支持
-
内核机制保障进程隔离和通信:
- 权限检查防止非法访问
- 消息传递需要参数验证和复制
- 内存隔离通过页表实现
- 资源引用计数防止泄漏
现代发展趋势:
- 容器化与命名空间:更细粒度的进程隔离
- Wayland与合成器:更安全的图形架构
- 微内核与能力安全:最小特权原则
- 硬件虚拟化:基于硬件的强隔离
理解进程、PCB和窗口嵌入的关系对于开发复杂的多进程GUI应用、调试系统级问题以及理解现代操作系统架构都至关重要。这些机制共同构成了现代计算环境的基础,平衡了性能、安全性和灵活性的需求。
1899

被折叠的 条评论
为什么被折叠?



