windows driver - DeviceIoControl 用法

article/2025/10/14 18:30:30

原文地址:https://www.cnblogs.com/lsh123/p/7354573.html

之前写过一篇关于通过DeviceIoControl函数来使应用程序与驱动程序通信的博客,这次再通过这个完整的代码来简要疏通总结一下。

   这种通信方式,就是驱动程序和应用程序自定义一种IO控制码,然后调用DeviceIoControl函数,IO管理器会产生一个MajorFunction 为IRP_MJ_DEVICE_CONTROL(DeviceIoControl函数会产生此IRP),MinorFunction 为自己定义的控制码的IRP,系统就调用相应的处理IRP_MJ_DEVICE_CONTROL的派遣函数,你在派遣函数中判断MinorFunction ,是自定义的控制码你就进行相应的处理。

  一.先谈一下这个定义IO控制码 ,其实可以看作是一种通信协议。

       看看CTL_CODE原型:

  #define CTL_CODE( DeviceType, Function, Method, Access ) ( \
  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
  )

   可以看到,这个宏四个参数,自然是一个32位分成了4部分,高16位存储设备类型,14~15位访问权限,2~13位操作功能,最后0,1两位就是确定缓冲区是如何与I/O和文件系统数据缓冲区进行数据传递方式,最常见的就是METHOD_BUFFERED。

       自定义CTL_CODE:

  #define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

  IOCTL_Device_Function:生成的IRP的MinorFunction

  DeviceType:设备对象的类型。设备类型可参考:http://blog.csdn.net/liyun123gx/article/details/38058965

  Function :自定义的IO控制码。自己定义时取0x800到0xFFF,因为0x0到0x7FF是微软保留的。

  Method :数据的操作模式。

              METHOD_BUFFERED:缓冲区模式

              METHOD_IN_DIRECT:直接写模式

              METHOD_OUT_DIRECT:直接读模式

              METHOD_NEITHER :Neither模式

Access:访问权限,可取值有:

            FILE_ANY_ACCESS:表明用户拥有所有的权限

            FILE_READ_DATA:表明权限为只读

            FILE_WRITE_DATA:表明权限为可写

            也可以 FILE_WRITE_DATA | FILE_READ_DATA:表明权限为可读可写,但还没达到FILE_ANY_ACCESS的权限。

  

  

       继续介绍这个缓冲区数据传递方式Method:

  Method表示Ring3/Ring0的通信中的内存访问方式,有四种方式:
  #define METHOD_BUFFERED                0  
  #define METHOD_IN_DIRECT               1  
  #define METHOD_OUT_DIRECT              2  
  #define METHOD_NEITHER                  3  


       (1)如果使用METHOD_BUFFERED,表示系统将用户的输入输出都经过pIrp->AssociatedIrp.SystemBuffer来缓冲,因此这种方式的通信比较安全。

  METHOD_BUFFERED方式相当于对Ring3的输入输出都进行了缓冲。

       METHOD_BUFFERED方式(借图):

       


  (2)如果使用METHOD_IN_DIRECT或METHOD_OUT_DIRECT方式,表示系统会将输入缓冲在pIrp->AssociatedIrp.SystemBuffer中,并将输出缓冲区锁定,然后在内核模式下重新映射一段地址,这样也是比较安全的。

  METHOD_IN_DIRECT和METHOD_OUT_DIRECT可称为"直接方式",是指系统依然对Ring3的输入缓冲区进行缓冲,但是对Ring3的输出缓冲区并没有缓冲,而是在内核中进行了锁定。这样Ring3输出缓冲区在驱动程序完成I/O请求之前,都是无法访问的,从一定程度上保障了安全性。如图21.1.14所示。
这两种方式,对于Ring3的输入缓冲区和METHOD_BUFFERED方式是一致的。对于Ring3的输出缓冲区,首先由系统锁定,并使用pIrp->MdlAddress来描述这段内存,驱动程序需要使用MmGetSystemAddressForMdlSafe函数将这段内存映射到内核内存地址(OutputBuffer),然后可以直接写入OutputBuffer地址,最终在驱动派遣例程返回后,由系统解除这段内存的锁定。
 
  METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的内存访问
  8METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的区别,仅在于打开设备的权限上,当以只读权限打开设备时,METHOD_IN_DIRECT方式的IoControl将会成功,而METHOD_OUT_DIRECT方式将会失败。如果以读写权限打开设备,两种方式都会成功。

  METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式(借图):

       


  (3)如果使用METHOD_NEITHER方式,"其他方式",虽然通信的效率提高了,但是不够安全。驱动的派遣函数中输入缓冲区可以通过I/O堆栈(IO_STACK_LOCATION)的stack->Parameters.DeviceIo Control.Type3InputBuffer得到。输出缓冲区可以通过pIrp->UserBuffer得到。由于驱动中的派遣函数不能保证传递进来的用户输入和输出地址,因此最好不要直接去读写这些地址的缓冲区。应该在读写前使用ProbeForRead和ProbeForWrite函数探测地址是否可读和可写。

  METHOD_ NEITHER方式是不进行缓冲的,在驱动中可以直接使用Ring3的输入输出内存地址,

  驱动程序可以通过pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer得到Ring3的输入缓冲区地址(其中pIrpStack是IoGetCurrentIrpStackLocation(pIrp)的返回);通过pIrp-> UserBuffer得到Ring3的输出缓冲区地址。
  由于METHOD_NEITHER方式并不安全,因此最好对Type3InputBuffer读取之前使用ProbeForRead函数进行探测,对UserBuffer写入之前使用ProbeForWrite函数进行探测,当没有发生异常时,再进行读取和写入操作。

  METHOD_NEITHER方式(借图):

  




  二 .定义驱动设备名,符号链接名
       定义好了IO控制码CTL_CODE,第二步驱动程序还要准备驱动设备名和符号链接名。     

    关于在Ring0层中要设置驱动设备名的同时还要设置符号链接名的原因,是因为只有符号链接名才可以被用户模式下的应用程序识别。

    windows下的设备是以"\Device\[设备名]”形式命名的。例如磁盘分区的c盘,d盘的设备名称就是"\Device\HarddiskVolume1”,"\Device\HarddiskVolume2”, 当然也可以不指定设备名称。                               如果IoCreateDevice中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。例如"\Device\00000001"。\Device\[设备名],不容易记忆,通常符号链接可以理解为设备的别名,更重要的是设备名,只能被内核模式下的其他驱动所识别,而别名可以被用户模式下的应用程序识别,例如c盘,就是名为"c:"的符号链接,其真正的设备对象是"\Device\HarddiskVolume1”,所以在写驱动时候,一般我们创建符号链接,即使驱动中没有用到,这也算是一个好的习惯吧。

    驱动中符号链接名是这样写的
    L"\\??\\HelloDDK" --->\??\HelloDDK

    或者
    L"\\DosDevices\\HelloDDK"--->\DosDevices\HelloDDK


    在应用程序中,符号链接名:
    L"\\\\.\\HelloDDK"-->\\.\HelloDDK

    DosDevices的符号链接名就是??, 所以"\\DosDevices\\XXXX"其实就是\\??\\XXXX

              

#define DEVICE_OBJECT_NAME  L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信

  三.将符号链接名与设备对象名称关联 ,等待IO控制码

    驱动程序要做的最后一步,先用IoCreateDevice函数创建设备对象,再用IoCreateSymbolicLink将符号链接名与设备对象名称关联 ,大功告成,等待IO控制码。

    

        //创建设备对象名称RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);//创建设备对象Status = IoCreateDevice(DriverObject,NULL,&DeviceObjectName,FILE_DEVICE_UNKNOWN,0, FALSE,&DeviceObject);if (!NT_SUCCESS(Status)){return Status;}//创建设备连接名称RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);//将设备连接名称与设备名称关联 Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);if (!NT_SUCCESS(Status)){IoDeleteDevice(DeviceObject);return Status;}        

  

  四.应用程序获取设备句柄,发送IO控制码。

    驱动程序铺垫打理好之后,应用程序就可以由符号链接名通过CreateFile函数获取到设备句柄DeviceHandle,再用本场的主角,DeviceIoControl通过这个DeviceHandle发送控制码了。

    先看看这两个函数:

   

BOOL WINAPI DeviceIoControl(_In_         HANDLE hDevice,       //CreateFile函数打开的设备句柄_In_         DWORD dwIoControlCode,//自定义的控制码_In_opt_     LPVOID lpInBuffer,    //输入缓冲区_In_         DWORD nInBufferSize,  //输入缓冲区的大小_Out_opt_    LPVOID lpOutBuffer,   //输出缓冲区_In_         DWORD nOutBufferSize, //输出缓冲区的大小_Out_opt_    LPDWORD lpBytesReturned, //实际返回的字节数,对应驱动程序中pIrp->IoStatus.Information。_Inout_opt_  LPOVERLAPPED lpOverlapped //重叠操作结构指针。同步设为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计
);HANDLE CreateFile(LPCTSTR lpFileName,                         //打开的文件名DWORD dwDesiredAccess,                    //访问权限DWORD dwShareMode,                      //共享模式LPSECURITY_ATTRIBUTES lpSecurityAttributes,   //安全属性DWORD dwCreationDisposition,               //文件存在与不存在时的文件创建模式DWORD dwFlagsAndAttributes,                //文件属性设定(隐藏、只读、压缩、指定为系统文件等)HANDLE hTemplateFile                       //文件副本句柄
);

  

  最后总结一下DeviceIoControl的通信流程:

    1.驱动程序和应用程序自定义好IO控制码 (CTL_CODE宏 四个参数,32位,4部分,存储设备类型,访问权限,操作功能,缓冲区数据传递方式(四种))

    2.驱动程序定义驱动设备名,符号链接名, 将符号链接名与设备对象名称关联 ,等待IO控制码(IoCreateDevice,IoCreateSymbolicLink)

    3.应用程序由符号链接名通过CreateFile函数获取到设备句柄DeviceHandle,再用本场的主角,DeviceIoControl通过这个设备句柄发送控制码给派遣函数。

  源代码:

  BufferedIO.h

  

#pragma once
#include <ntifs.h>#define CTL_SYS \CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)#define DEVICE_OBJECT_NAME  L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);

  

BufferedIO.c

#include "BufferedIO.h"NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{NTSTATUS Status = STATUS_SUCCESS;PDEVICE_OBJECT  DeviceObject = NULL;UNICODE_STRING  DeviceObjectName;UNICODE_STRING  DeviceLinkName;ULONG			i;//   栈//   堆//   全局(global Static Const)DriverObject->DriverUnload = DriverUnload;//创建设备对象名称RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);//创建设备对象Status = IoCreateDevice(DriverObject,NULL,&DeviceObjectName,FILE_DEVICE_UNKNOWN,0, FALSE,&DeviceObject);if (!NT_SUCCESS(Status)){return Status;}//创建设备连接名称RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);//将设备连接名称与设备名称关联 Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);if (!NT_SUCCESS(Status)){IoDeleteDevice(DeviceObject);return Status;}//设计符合我们代码的派遣历程	for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++){DriverObject->MajorFunction[i] = PassThroughDispatch;   //函数指针}DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;return Status;
}
//派遣历程 
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject,PIRP Irp)
{Irp->IoStatus.Status = STATUS_SUCCESS;     //LastError()Irp->IoStatus.Information = 0;             //ReturnLength IoCompleteRequest(Irp, IO_NO_INCREMENT);   //将Irp返回给Io管理器return STATUS_SUCCESS;
}
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{NTSTATUS Status;ULONG_PTR Informaiton = 0;PVOID InputData = NULL;ULONG InputDataLength = 0;PVOID OutputData = NULL;ULONG OutputDataLength = 0;ULONG IoControlCode = 0;PIO_STACK_LOCATION  IoStackLocation = IoGetCurrentIrpStackLocation(Irp);  //Irp堆栈	IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;InputData  = Irp->AssociatedIrp.SystemBuffer;OutputData = Irp->AssociatedIrp.SystemBuffer;InputDataLength  = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;switch (IoControlCode){case CTL_SYS:{if (InputData != NULL&&InputDataLength > 0){DbgPrint("%s\r\n", InputData);}if (OutputData != NULL&&OutputDataLength >= strlen("Ring0->Ring3") + 1){memcpy(OutputData, "Ring0->Ring3", strlen("Ring0->Ring3") + 1);Status = STATUS_SUCCESS;Informaiton = strlen("Ring0->Ring3") + 1;}else{Status = STATUS_INSUFFICIENT_RESOURCES;   //内存不够Informaiton = 0;}break;}default:break;}Irp->IoStatus.Status = Status;             //Ring3 GetLastError();Irp->IoStatus.Information = Informaiton;IoCompleteRequest(Irp, IO_NO_INCREMENT);  //将Irp返回给Io管理器return Status;                            //Ring3 DeviceIoControl()返回值
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{UNICODE_STRING  DeviceLinkName;PDEVICE_OBJECT	v1 = NULL;PDEVICE_OBJECT  DeleteDeviceObject = NULL;RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);IoDeleteSymbolicLink(&DeviceLinkName);DeleteDeviceObject = DriverObject->DeviceObject;while (DeleteDeviceObject != NULL){v1 = DeleteDeviceObject->NextDevice;IoDeleteDevice(DeleteDeviceObject);DeleteDeviceObject = v1;}
}

  

IO.cpp

// 缓冲区IO.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include <windows.h>
#define DEVICE_LINK_NAME    L"\\\\.\\BufferedIODevcieLinkName"#define CTL_SYS \CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
int main()
{HANDLE DeviceHandle = CreateFile(DEVICE_LINK_NAME,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (DeviceHandle==INVALID_HANDLE_VALUE){return 0;}char BufferData = NULL;DWORD ReturnLength = 0;BOOL IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,"Ring3->Ring0",strlen("Ring3->Ring0")+1,(LPVOID)BufferData,0,&ReturnLength,NULL);if (IsOk == FALSE){int LastError = GetLastError();if (LastError == ERROR_NO_SYSTEM_RESOURCES){char BufferData[MAX_PATH] = { 0 };IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,"Ring3->Ring0",strlen("Ring3->Ring0") + 1,(LPVOID)BufferData,MAX_PATH,&ReturnLength,NULL);if (IsOk == TRUE){printf("%s\r\n", BufferData);}}}if (DeviceHandle != NULL){CloseHandle(DeviceHandle);DeviceHandle = NULL;}printf("Input AnyKey To Exit\r\n");getchar();return 0;
}

http://chatgpt.dhexx.cn/article/MFM2Modw.shtml

相关文章

@ControllerAdvice 用法

ControllerAdvice 用法 顾名思义&#xff0c;ControllerAdvice就是Controller 的增强版。ControllerAdvice主要用来处理全局数据&#xff0c;一般搭配ExceptionHandler、ModelAttribute以及InitBinder使用。 全局异常处理 ControllerAdvice最常见的使用场景就是全局异常处理…

驱动开发:应用DeviceIoContro模板精讲

在笔者上一篇文章《驱动开发&#xff1a;应用DeviceIoContro开发模板》简单为大家介绍了如何使用DeviceIoContro模板快速创建一个驱动开发通信案例&#xff0c;但是该案例过于简单也无法独立加载运行&#xff0c;本章将继续延申这个知识点&#xff0c;通过封装一套标准通用模板…

驱动开发:应用DeviceIoContro开发模板

内核中执行代码后需要将结果动态显示给应用层的用户&#xff0c;DeviceIoControl 是直接发送控制代码到指定的设备驱动程序&#xff0c;使相应的移动设备以执行相应的操作的函数&#xff0c;如下代码是一个经典的驱动开发模板框架&#xff0c;在开发经典驱动时会用到的一个通用…

deviceiocontrol(deviceiocontrol函数)

如何用DeviceIOControl读取并口信息 应用程序和驱动程序的通信过程是&#xff1a;应用程序使用CreateFile函数打开设备&#xff0c;然后用DeviceIoControl与驱动程序进行通信&#xff0c;包括读和写两种操作。还可以用ReadFile读数据用WriteFile写数据。操作完毕时用CloseHandl…

DeviceIOControl详解-各个击破

DeviceIoControl这个api我们用的不多&#xff0c;但是很重要&#xff0c;有时会帮助我们实现一些特别的需求, 如获取硬件设备信息、与硬件设备通信&#xff08;读写数据&#xff09;等&#xff0c;对照msdn&#xff0c;下面我们详细解释一下这个api的用法&#xff08;有什么错误…

DeviceIoControl 函数详细解析

前言&#xff1a; 最近需要对Windows中的设备进行编程操作&#xff0c;其中涉及到非常重要的函数DeviceIoControl&#xff0c;在使用的时候也比较的复杂&#xff0c;国内这一块中文资料比较少&#xff0c;在学习之余顺便将其翻译出来&#xff0c;以供参考&#xff0c;如有错误&…

操作系统原理(一)——自举过程

操作系统&#xff0c;这个名词每个人都不会陌生&#xff0c;但又总觉得似乎没有了解很深&#xff0c;这一系列博客将会从零开始强化你对操作系统的认知。 1. 什么是操作系统&#xff1f; 答&#xff1a;操作系统是介于硬件与用户&#xff08;也就是程序和人&#xff09;之间的…

【操作系统基础知识 一】操作系统基本原理

学生时代没有好好学习操作系统&#xff0c;悔不当初&#xff0c;现在重新捡起来看看&#xff0c;才发现日常有很多事情都可以解释了&#xff0c;例如为什么Linux快&#xff01;并发是什么&#xff1f;操作系统到底干啥活&#xff0c;这篇blog是王道的学习笔记&#xff1a; 操作…

操作系统原理,进程的基本状态,运行态,就绪态,等待态与转换模型,进程的其他状态,创建,终止,挂起与转换模型,Linux进程状态模型示例

操作系统原理&#xff0c;进程的基本状态&#xff0c;运行态&#xff0c;就绪态&#xff0c;等待态与转换模型&#xff0c;进程的其他状态&#xff0c;创建&#xff0c;终止&#xff0c;挂起与转换模型&#xff0c;Linux进程状态模型示例 一、进程的三种基本状态&#xff1a; …

Linux操作系统基础原理

计算机系统 1.计算机体系层次 计算机自身是由众多电子元器件构成&#xff0c;硬件本身提供给用户的接口十分底层复杂&#xff0c;使用很不方便。在硬件之上的操作系统将硬件接口抽象封装为比较直观&#xff0c;用户容易调用的接口&#xff1b;用户开发应用程序&#xff0c;通过…

操作系统原理实验——实验一 Linux基本操作

1、实验目的 &#xff08;1&#xff09;熟悉Linux下的基本操作&#xff0c;学会使用各种Shell命令去操作Linux&#xff0c;对Linux有一个感性认识。 &#xff08;2&#xff09;学会使用vi编辑器编辑简单的C语言程序&#xff0c;并能对其编译和调试。 2、实验预备内容 &#…

Linux 操作系统原理 — 操作系统的本质

目录 文章目录 目录操作系统的起源操作系统和高级编程语言使硬件抽象化操作系统的起源 在操作系统尚不存在的年代,人们通过各种按钮来控制计算机,这一过程非常麻烦。于是,有人开发出了仅仅具有加载和运行功能的监控程序(Supervisor),这就是操作系统的原型。 通过监控程…

操作系统原理之多进程、多线程与并发

文章目录 (1)什么是进程&#xff1f;(2)进程的状态(3)OS何时介入进程调度&#xff1f;(4)多进程的意义(5)并行和并发(6)进程间通信(7)死锁(8)什么是线程&#xff1f;(9)多线程的意义(10)进程和线程的区别(11)Java线程和OS线程的关系 提示&#xff1a;以下是本篇文章正文内容&am…

【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第二章

前言 此书在最后的附录B中&#xff0c;有给出部分重难点部分的参考答案。会在最后放上图片。如果想要此书习题答案&#xff0c;可点以下链接&#xff1a;为一个压缩包&#xff0c;以图片形式&#xff0c;习题图片按章节排序&#xff0c;答案图片按书页排序。 《操作系统原理…

操作系统原理——第六章:页面置换算法

文章目录 1. 功能与目标2. 实验设置与评价方法3. 局部页面置换算法3.1 最优页面置换算法&#xff08;OPT&#xff0c;optimal&#xff09;3.2 先进先出算法&#xff08;FIFO&#xff09;3.3 最近最久未使用算法&#xff08;LRU&#xff0c;Least Recently Used&#xff09;3.4 …

操作系统原理模拟实验(基于C/C++模拟处理机调度、存储管理和文件系统)

目录 引言一、处理机调度模拟1、下载链接2、目的与要求3、截图示例 二、存储管理模拟动态分区分配1、下载链接2、目的与要求3、截图示例 分页存储地址转换1、下载链接2、目的与要求3、截图示例 三、文件系统模拟1、下载链接2、目的与要求3、截图示例 引言 包含多个实验的完整源…

操作系统原理总结

转载&#xff1a;https://blog.csdn.net/yanglingwell/article/details/53745758 操作系统原理总结 made by 杨领well (yanglingwellsina.com) 一、基础知识点 1. 操作系统的资源管理技术 资源管理解决物理资源数量不足和合理分配资源这两个问题。 操作系统虚拟机为用户提供…

操作系统原理:覆盖技术、交换技术、虚拟内存概要

随着时间的推移&#xff0c;程序不断地更新&#xff0c;规模不断增长&#xff0c;运行的时候可能会发现内存会越来越不够用。所以希望一个容量大&#xff0c;更快&#xff0c;更便宜&#xff0c;数据不易丢失的存储器。 首先想到的就是硬盘&#xff0c;所以在硬盘的基础上建立了…

操作系统原理、实现与实践课后习题参考答案(已完结)

习题二–系统接口 通向操作系统内核的大门 1.调用fork()的父子进程执行“同样”的代码&#xff0c;如何理解”同样“&#xff1f; 答&#xff1a; fork()函数为系统调用&#xff0c;用于创建进程。创建的进程与原来进程几乎完全相同. 一个进程调用fork&#xff08;&#xff09…