windows是啥?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了windows是啥?相关的知识,希望对你有一定的参考价值。
windows是操作系统。
Windows操作系统,是由美国微软公司(Microsoft)研发的操作系统,问世于1985年。起初是MS-DOS模拟环境,后续由于微软对其进行不断更新升级,提升易用性,使Windows成为了应用最广泛的操作系统。
Windows采用了图形用户界面(GUI),比起从前的MS-DOS需要输入指令使用的方式更为人性化。随着计算机硬件和软件的不断升级,Windows也在不断升级,从架构的16位、32位再到64位。
特点:
操作系统是人使用计算机硬件沟通的平台,没有良好的人机操作性,就难以吸引广大用户使用。手机领域,诺基亚手机能够占据手机市场半壁江山,手机操作系统互动性良好是其成功的重要因素之一,而其迅速的衰败也是因为操作系统的落伍。
Windows操作系统能够作为个人计算机的主流操作系统,其优异的人机操作性是重要因素。Windows操作系统界面友好,窗口制作优美,操作动作易学,多代系统之间有良好的传承,计算机资源管理效率较高,效果较好。
参考技术Awindows是操作系统。
Windows操作系统,是由美国微软公司(Microsoft)研发的操作系统,问世于1985年。起初是MS-DOS模拟环境,后续由于微软对其进行不断更新升级,提升易用性,使Windows成为了应用最广泛的操作系统。
Windows采用了图形用户界面(GUI),比起从前的MS-DOS需要输入指令使用的方式更为人性化。随着计算机硬件和软件的不断升级,Windows也在不断升级,从架构的16位、32位再到64位。
特点:
操作系统是人使用计算机硬件沟通的平台,没有良好的人机操作性,就难以吸引广大用户使用。手机领域,诺基亚手机能够占据手机市场半壁江山,手机操作系统互动性良好是其成功的重要因素之一,而其迅速的衰败也是因为操作系统的落伍。
Windows操作系统能够作为个人计算机的主流操作系统,其优异的人机操作性是重要因素。Windows操作系统界面友好,窗口制作优美,操作动作易学,多代系统之间有良好的传承,计算机资源管理效率较高,效果较好。
Windows 最接近 fork() 的是啥?
【中文标题】Windows 最接近 fork() 的是啥?【英文标题】:What is the closest thing Windows has to fork()?Windows 最接近 fork() 的是什么? 【发布时间】:2009-06-12 06:36:21 【问题描述】:我想这个问题说明了一切。
我想在 Windows 上分叉。最相似的操作是什么,如何使用。
【问题讨论】:
【参考方案1】:Cygwin 在 Windows 上具有全功能的 fork()。因此,如果您可以接受使用 Cygwin,那么在性能不成问题的情况下问题就解决了。
否则你可以看看Cygwin是如何实现fork()的。来自一个相当古老的 Cygwin 架构doc:
5.6。流程创建 Cygwin 中的 fork 调用特别有趣 因为它不能很好地映射到 Win32 API。这使它非常 难以正确实施。 目前,Cygwin fork 是 非写时复制实现 类似于早期存在的 UNIX 风格。
当一个 父进程派生一个子进程 是父初始化了一个空间 在 Cygwin 进程表中 孩子。然后它创建一个暂停 子进程使用 Win32 CreateProcess 调用。接下来是家长 进程调用 setjmp 保存自己的 上下文并设置一个指向 this 的指针 一个 Cygwin 共享内存区域(共享 在所有 Cygwin 任务中)。然后填充 在孩子的 .data 和 .bss 部分 通过从自己的地址空间复制 进入被停学儿童的地址 空间。在孩子的地址空间之后 被初始化,孩子在运行时 父级等待互斥锁。孩子 发现它已被分叉并且 使用保存的跳转缓冲区进行长跳转。 然后孩子将互斥锁设置为 父母正在等待并阻止 另一个互斥体。这是信号 父级复制其堆栈和堆 进入孩子,之后它 释放孩子所在的互斥锁 等待并从叉子返回 称呼。终于,孩子从 阻塞最后一个互斥体,重新创建 任何传递给它的内存映射区域 通过共享区域,并从返回 fork 自己。
虽然我们对如何 通过以下方式加快我们的分叉实施 减少上下文的数量 在父子之间切换 过程中,fork 几乎肯定会 Win32下总是效率低下。 幸运的是,在大多数情况下 产生由提供的呼叫家族 Cygwin 可以替换为 fork/exec 对只有一点点 努力。这些调用清晰地映射在顶部 Win32 API 的。结果,他们 效率更高。改变 编译器的驱动程序调用 spawn 而不是 fork 是微不足道的 改变和增加编译 速度由百分之二十到百分之三十 我们的测试。
然而,spawn 和 exec 展示了他们的 自己的一套困难。因为那里 是没有办法做一个实际的执行下 Win32,Cygwin要自己发明 进程 ID (PID)。结果,当 一个进程执行多个exec 调用,会有多个窗口 与单个 Cygwin 关联的 PID PID。在某些情况下,每个存根 这些 Win32 进程可能会持续存在, 等待他们的 exec'd Cygwin 进程退出。
听起来工作量很大,不是吗?是的,它很慢。
编辑:文档已过时,请参阅此出色的 answer 以获取更新
【讨论】:
如果您想在 Windows 上编写 Cygwin 应用程序,这是一个很好的答案。但总的来说,这不是最好的做法。从根本上说,*nix 和 Windows 进程和线程模型是完全不同的。 CreateProcess() 和 CreateThread() 是通常等效的 API 开发人员应该记住,这是一种不受支持的机制,而且 IIRC 确实倾向于在系统上的其他进程使用代码注入时中断。 不同的实现链接不再有效。 编辑后仅保留其他答案链接 @Foredecker,实际上您不应该这样做,即使您正在尝试编写“cygwin 应用程序”。它试图模仿 Unix 的fork
,但它通过 leaky solution 来实现这一点,你必须为意外情况做好准备。【参考方案2】:
我当然不知道这方面的细节,因为我从来没有这样做过,但是本机 NT API 具有派生进程的能力(Windows 上的 POSIX 子系统需要这种能力 - 我不确定是否甚至不再支持 POSIX 子系统。
搜索 ZwCreateProcess() 应该可以获得更多详细信息 - 例如 this bit of information from Maxim Shatskih:
这里最重要的参数是SectionHandle。如果这个参数 为 NULL,内核将分叉当前进程。否则,这 参数必须是创建的 SEC_IMAGE 部分对象的句柄 调用 ZwCreateProcess() 之前的 EXE 文件。
虽然注意Corinna Vinschen indicates that Cygwin found using ZwCreateProcess() still unreliable:
Iker Arizmendi 写道:
> Because the Cygwin project relied solely on Win32 APIs its fork > implementation is non-COW and inefficient in those cases where a fork > is not followed by exec. It's also rather complex. See here (section > 5.6) for details: > > http://www.redhat.com/support/wpapers/cygnus/cygnus_cygwin/architecture.html
这个文件比较老了,10 年左右。虽然我们还在使用 Win32 调用模拟 fork,方法发生了显着变化。 特别是,我们不会在挂起状态下创建子进程 不再,除非特定的数据结构需要在 父母在他们被复制到孩子之前。在当前的 1.5.25 释放暂停的孩子的唯一情况是在 父母。即将发布的 1.7.0 版本根本不会暂停。
不使用 ZwCreateProcess 的一个原因是直到 1.5.25 发布我们仍然支持 Windows 9x 用户。然而,两个 尝试在基于 NT 的系统上使用 ZwCreateProcess 失败了 原因或其他。
如果这些东西会更好或根本没有,那就太好了 记录在案,尤其是几个数据结构以及如何连接 处理到子系统。虽然 fork 不是 Win32 概念,但我不是 看到让 fork 更容易实现是一件坏事。
【讨论】:
这是错误的答案。 CreateProcess() 和 CreateThread() 是一般等价物。 Interix 在 Windows Vista Enterprise/Ultimate 中作为“UNIX 应用程序的子系统”提供:en.wikipedia.org/wiki/Interix @Foredecker - 这可能是一个错误的答案,但 CreateProcess()/CreateThread() 也可能是错误的。这取决于一个人是在寻找“Win32 做事方式”还是“尽可能接近 fork() 语义”。 CreateProcess() 的行为与 fork() 明显不同,这就是 cygwin 需要做大量工作来支持它的原因。 @jon:我尝试修复链接并将相关文本复制到答案中(因此将来断开的链接不是问题)。但是,这个答案来自很久以前,我不能 100% 确定我今天找到的引用是我在 2009 年所指的。 如果人们想要“fork
和直接的exec
”,那么也许 CreateProcess 是一个候选者。但是fork
没有exec
通常是可取的,这就是司机要求真正的fork
。【参考方案3】:
嗯,windows 并没有什么类似的东西。特别是因为在 *nix 中可以使用 fork 从概念上创建线程或进程。
所以,我不得不说:
CreateProcess()
/CreateProcessEx()
和
CreateThread()
(我听说对于 C 应用程序,_beginthreadex()
更好)。
【讨论】:
【参考方案4】:人们尝试在 Windows 上实现 fork。这是我能找到的最接近它的东西:
取自:http://doxygen.scilab.org/5.3/d0/d8f/forkWindows_8c_source.html#l00216
static BOOL haveLoadedFunctionsForFork(void);
int fork(void)
HANDLE hProcess = 0, hThread = 0;
OBJECT_ATTRIBUTES oa = sizeof(oa) ;
MEMORY_BASIC_INFORMATION mbi;
CLIENT_ID cid;
USER_STACK stack;
PNT_TIB tib;
THREAD_BASIC_INFORMATION tbi;
CONTEXT context =
CONTEXT_FULL |
CONTEXT_DEBUG_REGISTERS |
CONTEXT_FLOATING_POINT
;
if (setjmp(jenv) != 0) return 0; /* return as a child */
/* check whether the entry points are
initilized and get them if necessary */
if (!ZwCreateProcess && !haveLoadedFunctionsForFork()) return -1;
/* create forked process */
ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa,
NtCurrentProcess(), TRUE, 0, 0, 0);
/* set the Eip for the child process to our child function */
ZwGetContextThread(NtCurrentThread(), &context);
/* In x64 the Eip and Esp are not present,
their x64 counterparts are Rip and Rsp respectively. */
#if _WIN64
context.Rip = (ULONG)child_entry;
#else
context.Eip = (ULONG)child_entry;
#endif
#if _WIN64
ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Rsp,
MemoryBasicInformation, &mbi, sizeof mbi, 0);
#else
ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Esp,
MemoryBasicInformation, &mbi, sizeof mbi, 0);
#endif
stack.FixedStackBase = 0;
stack.FixedStackLimit = 0;
stack.ExpandableStackBase = (PCHAR)mbi.BaseAddress + mbi.RegionSize;
stack.ExpandableStackLimit = mbi.BaseAddress;
stack.ExpandableStackBottom = mbi.AllocationBase;
/* create thread using the modified context and stack */
ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, hProcess,
&cid, &context, &stack, TRUE);
/* copy exception table */
ZwQueryInformationThread(NtCurrentThread(), ThreadBasicInformation,
&tbi, sizeof tbi, 0);
tib = (PNT_TIB)tbi.TebBaseAddress;
ZwQueryInformationThread(hThread, ThreadBasicInformation,
&tbi, sizeof tbi, 0);
ZwWriteVirtualMemory(hProcess, tbi.TebBaseAddress,
&tib->ExceptionList, sizeof tib->ExceptionList, 0);
/* start (resume really) the child */
ZwResumeThread(hThread, 0);
/* clean up */
ZwClose(hThread);
ZwClose(hProcess);
/* exit with child's pid */
return (int)cid.UniqueProcess;
static BOOL haveLoadedFunctionsForFork(void)
HANDLE ntdll = GetModuleHandle("ntdll");
if (ntdll == NULL) return FALSE;
if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
return TRUE;
ZwCreateProcess = (ZwCreateProcess_t) GetProcAddress(ntdll,
"ZwCreateProcess");
ZwQuerySystemInformation = (ZwQuerySystemInformation_t)
GetProcAddress(ntdll, "ZwQuerySystemInformation");
ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t)
GetProcAddress(ntdll, "ZwQueryVirtualMemory");
ZwCreateThread = (ZwCreateThread_t)
GetProcAddress(ntdll, "ZwCreateThread");
ZwGetContextThread = (ZwGetContextThread_t)
GetProcAddress(ntdll, "ZwGetContextThread");
ZwResumeThread = (ZwResumeThread_t)
GetProcAddress(ntdll, "ZwResumeThread");
ZwQueryInformationThread = (ZwQueryInformationThread_t)
GetProcAddress(ntdll, "ZwQueryInformationThread");
ZwWriteVirtualMemory = (ZwWriteVirtualMemory_t)
GetProcAddress(ntdll, "ZwWriteVirtualMemory");
ZwClose = (ZwClose_t) GetProcAddress(ntdll, "ZwClose");
if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
return TRUE;
else
ZwCreateProcess = NULL;
ZwQuerySystemInformation = NULL;
ZwQueryVirtualMemory = NULL;
ZwCreateThread = NULL;
ZwGetContextThread = NULL;
ZwResumeThread = NULL;
ZwQueryInformationThread = NULL;
ZwWriteVirtualMemory = NULL;
ZwClose = NULL;
return FALSE;
【讨论】:
请注意,大多数错误检查都丢失了 - 例如ZwCreateThread 返回一个 NTSTATUS 值,可以使用 SUCCEEDED 和 FAILED 宏检查。 如果fork
崩溃了,它会导致程序崩溃,还是线程只是崩溃了?如果它使程序崩溃,那么这并不是真正的分叉。只是好奇,因为我正在寻找一个真正的解决方案,并希望这可能是一个不错的选择。
我想指出提供的代码中有一个错误。 haveLoadedFunctionsForFork 是header中的全局函数,但是c文件中的静态函数。它们都应该是全球性的。目前 fork 崩溃,现在添加错误检查。
该站点已死,我不知道如何在自己的系统上编译该示例。我假设我缺少一些标题或包含错误的标题是吗? (示例未显示它们。)【参考方案5】:
在 Microsoft 推出新的“Windows 的 Linux 子系统”选项之前,CreateProcess()
是 Windows 与 fork()
最接近的东西,但 Windows 要求您指定在该进程中运行的可执行文件。
UNIX 进程的创建与 Windows 完全不同。它的fork()
调用基本上几乎完全复制了当前进程,每个进程都在自己的地址空间中,并继续分别运行它们。虽然进程本身不同,但它们仍在运行相同的程序。请参阅here 以了解fork/exec
模型的良好概述。
反过来说,Windows 中的CreateProcess()
相当于 UNIX 中的fork()/exec()
对函数。
如果您将软件移植到 Windows 并且不介意翻译层,Cygwin 提供了您想要的功能,但它相当笨拙。
当然,有了新的Linux subsystem,Windows 最接近fork()
的东西实际上 fork()
:-)
【讨论】:
那么,考虑到 WSL,我可以在普通的非 WSL 应用程序中使用fork
吗?【参考方案6】:
以下文档提供了有关将代码从 UNIX 移植到 Win32 的一些信息: https://msdn.microsoft.com/en-us/library/y23kc048.aspx
除其他外,它表明两个系统之间的进程模型有很大不同,建议在需要类似 fork() 的行为时考虑 CreateProcess 和 CreateThread。
【讨论】:
【参考方案7】:正如其他答案所提到的,NT(现代 Windows 版本的内核)相当于 Unix fork()。这不是问题。
问题在于,克隆进程的整个状态通常不是一件明智的事情。这在 Unix 世界中和在 Windows 中一样正确,但在 Unix 世界中,fork() 一直在使用,并且库旨在处理它。 Windows 库不是。
例如,系统 DLL kernel32.dll 和 user32.dll 维护到 Win32 服务器进程 csrss.exe 的专用连接。分叉后,该连接的客户端上有两个进程,这将导致问题。子进程应该通知 csrss.exe 它的存在并建立新的连接——但是没有接口可以做到这一点,因为这些库在设计时没有考虑到 fork()。
所以你有两个选择。一种是禁止使用 kernel32 和 user32 以及其他非设计为 fork 的库——包括任何直接或间接链接到 kernel32 或 user32 的库,这几乎是所有这些库。这意味着您根本无法与 Windows 桌面交互,并被困在自己独立的 Unixy 世界中。这是用于 NT 的各种 Unix 子系统所采用的方法。
另一种选择是诉诸某种可怕的 hack 来尝试让不知情的库与 fork() 一起工作。这就是 Cygwin 所做的。它创建一个新进程,让它初始化(包括使用 csrss.exe 注册自身),然后从旧进程复制大部分动态状态并希望最好。这ever 让我感到惊讶。它当然不能可靠地工作——即使它不会由于地址空间冲突而随机失败,您正在使用的任何库都可能会默默地处于损坏状态。当前接受的答案声称 Cygwin 有一个“功能齐全的 fork()”是……可疑的。
总结:在类似 Interix 的环境中,您可以通过调用 fork() 进行分叉。否则,请试着让自己摆脱做这件事的欲望。即使你的目标是 Cygwin,也不要使用 fork(),除非你必须这样做。
【讨论】:
【参考方案8】:“只要你想进行文件访问或 printf 然后 io 就会被拒绝”
你不能一边吃一边吃蛋糕……在 msvcrt.dll 中,printf() 基于控制台 API,它本身使用 lpc 与控制台子系统 (csrss.exe) 进行通信。与 csrss 的连接是在进程启动时启动的,这意味着任何“在中间”开始执行的进程都将跳过该步骤。除非您可以访问操作系统的源代码,否则尝试手动连接到 csrss 是没有意义的。相反,您应该创建自己的子系统,并相应地避免在使用 fork() 的应用程序中使用控制台功能。
一旦实现了自己的子系统,别忘了为子进程复制所有父进程的句柄;-)
“另外,除非您处于内核模式,否则您可能不应该使用 Zw* 函数,您可能应该使用 Nt* 函数。”
这是不正确的。在用户态访问时,Zw*** Nt*** 绝对没有区别;这些只是引用相同(相对)虚拟地址的两个不同 (ntdll.dll) 导出名称。ZwGetContextThread(NtCurrentThread(), &context);
通过调用 ZwGetContextThread 获取当前(正在运行的)线程的上下文是错误的,很可能会崩溃,并且(由于额外的系统调用)也不是完成任务的最快方法。【讨论】:
这似乎不是在回答主要问题,而是在回答其他几个不同的答案,为了清楚起见并更容易理解正在发生的事情,可能会更好地直接回答每个问题。 您似乎在假设 printf 总是写入控制台。【参考方案9】:您的最佳选择是CreateProcess() 或CreateThread()。有更多关于移植here的信息。
【讨论】:
【参考方案10】:在 Windows 上模拟 fork() 没有简单的方法。
我建议你改用线程。
【讨论】:
好吧,公平地说,实现fork
正是 CygWin 所做的。但是,如果您曾经阅读过他们如何做到这一点,那么您的“不容易”是一种严重的误解:-)【参考方案11】:
fork() 语义是必要的,当调用 fork() 时,子级需要访问父级的实际内存状态。我有一个软件,它依赖于调用即时 fork() 时内存复制的隐式互斥锁,这使得线程无法使用。 (这在现代 *nix 平台上通过 copy-on-write/update-memory-table 语义进行了模拟。)
在 Windows 上作为系统调用存在的最接近的是 CreateProcess。可以做的最好的事情是让父进程在将内存复制到新进程的内存空间期间冻结所有其他线程,然后解冻它们。我可以看到,Cygwin frok [sic] 类和 Eric des Courtis 发布的 Scilab 代码都没有线程冻结。
此外,除非您处于内核模式,否则您可能不应该使用 Zw* 函数,而应该使用 Nt* 函数。有一个额外的分支检查您是否处于内核模式,如果不是,则执行 Nt* 始终执行的所有边界检查和参数验证。因此,从用户模式调用它们的效率会稍微低一些。
【讨论】:
关于 Zw* 导出符号的非常有趣的信息,谢谢。 请注意,为了安全起见,来自用户空间的 Zw* 函数仍映射到内核空间中的 Nt* 函数。或者至少他们应该这样做。【参考方案12】:你说的最接近...让我想想...这一定是 fork() 我猜 :)
详情见Does Interix implement fork()?
【讨论】:
The Sad History of the Microsoft POSIX Subsystem 对 Interix 也很有启发。【参考方案13】:如果您只关心创建子进程并等待它,那么 process.h 中的 _spawn* API 可能就足够了。以下是有关此的更多信息:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h
【讨论】:
【参考方案14】:大多数 hacky 解决方案都已过时。 Winnie the fuzzer 有一个 fork 版本,可以在当前版本的 Windows 10 上运行(但这需要系统特定的偏移量并且也很容易破坏)。
https://github.com/sslab-gatech/winnie/tree/master/forklib
【讨论】:
以上是关于windows是啥?的主要内容,如果未能解决你的问题,请参考以下文章