SpringMvc源码分析

news2025/1/2 2:41:41

概述

用户的请求,是如何被 DispatcherServlet 处理的
先看图
在这里插入图片描述

从图中可以看到请求首先是被 DispatcherServlet 所处理,但是实际上,FrameworkServlet 先被触发

我们看下处理各种请求的方法

@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate POST requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate PUT requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doPut(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate DELETE requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate OPTIONS requests to {@link #processRequest}, if desired.
	 * <p>Applies HttpServlet's standard OPTIONS processing otherwise,
	 * and also if there is still no 'Allow' header set after dispatching.
	 * @see #doService
	 */
	@Override
	protected void doOptions(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
			processRequest(request, response);
			if (response.containsHeader("Allow")) {
				// Proper OPTIONS response coming from a handler - we're done.
				return;
			}
		}

		// Use response wrapper in order to always add PATCH to the allowed methods
		super.doOptions(request, new HttpServletResponseWrapper(response) {
			@Override
			public void setHeader(String name, String value) {
				if ("Allow".equals(name)) {
					value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
				}
				super.setHeader(name, value);
			}
		});
	}

	/**
	 * Delegate TRACE requests to {@link #processRequest}, if desired.
	 * <p>Applies HttpServlet's standard TRACE processing otherwise.
	 * @see #doService
	 */
	@Override
	protected void doTrace(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (this.dispatchTraceRequest) {
			processRequest(request, response);
			if ("message/http".equals(response.getContentType())) {
				// Proper TRACE response coming from a handler - we're done.
				return;
			}
		}
		super.doTrace(request, response);
	}
	
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//获得请求方法
		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		//处理 PATCH 请求
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);
		}
		else {
		//调用父类,处理其它请求
			super.service(request, response);
		}
	}

最终这些方法都会调用processRequest(request, response)方法

我门看下super.service(request, response)方法

 protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

doGet & doPost & doPut & doDelete
这四个方法,都是直接调用 #processRequest(HttpServletRequest request, HttpServletResponse response) 方法,处理请求。代码如下:


@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	processRequest(request, response);
}

@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	processRequest(request, response);
}

@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	processRequest(request, response);
}

@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	processRequest(request, response);
}

看下processRequest方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
			//代码执行逻辑
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			//打印日志
			logResult(request, response, failureCause, asyncManager);
			//发布事件
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

我们看下doService是一个抽象方法
在这里插入图片描述
而DispatcherServlet 继承了FrameworkServlet 并重写了DoService方法
在这里插入图片描述
这个时候真正到我们的DispatcherServlet

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
					//执行请求的分发
					doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

doDispatch方法核心

// DispatcherServlet.java

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
	
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// 1、获得请求对应的 HandlerExecutionChain 对象
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) { 
				noHandlerFound(processedRequest, response);
				return;
			}

			
			// 2、 获得当前 handler 对应的 HandlerAdapter 对象
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			
			String method = request.getMethod();
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}

			// 3、拦截器前置处理
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			
			// 4、 真正的调用 handler 方法,并返回视图
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}

			applyDefaultViewName(processedRequest, mv);
			// 5、拦截器后置处理 
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		} catch (Exception ex) {
			dispatchException = ex; 
		} catch (Throwable err) {
			dispatchException = new NestedServletException("Handler dispatch failed", err); 
		}

		// 6、处理调用结果
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	} catch (Exception ex) {
		// 7、拦截器以请求完成拦截
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	} catch (Throwable err) {
		7、拦截器以请求完成拦截
		triggerAfterCompletion(processedRequest, response, mappedHandler,
				new NestedServletException("Handler processing failed", err));
	} finally {
		if (asyncManager.isConcurrentHandlingStarted()) {
			if (mappedHandler != null) {
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		} else {
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}

getHandler(processedRequest)方法

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			//变量 HandlerMapping 数组
			for (HandlerMapping mapping : this.handlerMappings) {
			//获得请求对应的 HandlerExecutionChain 对象
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

getHandlerAdapter


private List<HandlerAdapter> handlerAdapters;    

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		// 遍历 HandlerAdapter 数组
		for (HandlerAdapter adapter : this.handlerAdapters) {
 			// 判断是否支持当前处理器
			if (adapter.supports(handler)) {
				// 如果支持,则返回
				return adapter;
			}
		}
	}

拦截器方法

// HandlerInterceptor.java

public interface HandlerInterceptor {

    /**
     * 拦截处理器,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行之前
     */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return true;
    }

    /**
     * 拦截处理器,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行成功之后
     */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
     * 拦截处理器,在 {@link HandlerAdapter} 执行完之后,无论成功还是失败
     *
     * 并且,只有 {@link #preHandle(HttpServletRequest, HttpServletResponse, Object)} 执行成功之后,才会被执行
     */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
    }

}

HandlerExecutionChain.class

//HandlerExecutionChain.class

/**
 * 应用拦截器的前置处理
 *
 * Apply preHandle methods of registered interceptors.
 * @return {@code true} if the execution chain should proceed with the
 * next interceptor or the handler itself. Else, DispatcherServlet assumes
 * that this interceptor has already dealt with the response itself.
 */
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // <1> 获得拦截器数组
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        // <2> 遍历拦截器数组
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            // <3> 前置处理
            if (!interceptor.preHandle(request, response, this.handler)) {
                // <3.1> 触发已完成处理
                triggerAfterCompletion(request, response, null);
                // 返回 false ,前置处理失败
                return false;
            }
            // <3.2> 标记 interceptorIndex 位置
            this.interceptorIndex = i;
        }
    }
    // <4> 返回 true ,前置处理成功
    return true;
}

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
		throws Exception {
    // 获得拦截器数组
    HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
        // 遍历拦截器数组
        for (int i = interceptors.length - 1; i >= 0; i--) { // 倒序
			HandlerInterceptor interceptor = interceptors[i];
			// 后置处理
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
}





void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
		throws Exception {
	// 获得拦截器数组
	HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
		// 遍历拦截器数组
		for (int i = this.interceptorIndex; i >= 0; i--) { // 倒序!!!
			HandlerInterceptor interceptor = interceptors[i];
			try {
				// 已完成处理
				interceptor.afterCompletion(request, response, this.handler, ex);
			} catch (Throwable ex2) { // 注意,如果执行失败,仅仅会打印错误日志,不会结束循环
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/582915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaEE进阶(5/28)SpringMVC

目录 1.什么是SpringMVC&#xff1f; 2.学习SpringMVC学习了什么&#xff1f; 3.SpringMVC核心1 4.SpringBoot传递参数 5.SpringBoot传递对象参数 6.SpringBoot传递表单 7.SpringBoot后端参数重命名 8.SpringBoot后端用来接收json对象 1.什么是SpringMVC&#xff1f; 1.…

如何在手机和平板中浏览三维实景模型?

对于数据量庞大的三维实景模型&#xff0c;想要在手机和平板中进行在线浏览并实现实时交互&#xff0c;一般平台很难实现这些功能。 四维轻云是一款操作简单、支持在线协作、在线展示及在线分享的三维实景模型在线浏览平台。在四维轻云平台中&#xff0c;用户可以创建项目&…

人生苦短,我用Python:如何入门Python的世界

人生苦短&#xff0c;我用Python&#xff1a;如何入门Python的世界 Python是一门非常简洁、易读、高效的编程语言&#xff0c;适合初学者入门。自从1991年问世以来&#xff0c;Python已经在全球范围内成为了众多工程师、科研人员、数据分析师等人群的首选编程语言。本文将详细…

数据库DBMS并发控制

pgsql&#xff08;PostgreSQL&#xff09;常用命令行操作_pgsql常用命令_石头wang的博客-CSDN博客 事务的操作和事务的性质 操作演示 三种典型数据不一致现象: 串行调度和可串行调度 串行调度 顾名思义 就是可以进行调度的意思 可串行调度 就是 一种和串行调度等价的并行调…

Quest 3初体验,或是苹果MR最大竞争对手

随着苹果MR临近&#xff0c;我们从彭博Mark Gurman了解到更多消息。昨日&#xff0c;Mark Gurman发布了Quest 3上手体验文章&#xff0c;并认为Quest 3可能是苹果MR头显最大的竞争对手。 1&#xff0c;Meta是XR头显领导者 尽管WWDC 23苹果MR将会成为最大的主角&#xff0c;但…

Android音视频开发2:So库适配总结

名词解析 Android NDK 是在SDK前⾯⼜加上了原⽣⼆字&#xff0c;即Native Development Kit&#xff0c;因此⼜被Google称为 NDK。C/C编写代码。so 为共享库,是shared object。 前⾔ ⽇常开发我们经常会使⽤到第三库&#xff0c;涉及到底层的语⾳&#xff0c;视频等都需要…

【虚拟机】VMware虚拟机安装Windows 10系统 详细教程

大家好&#xff0c;我是雷工&#xff01; 由于购买的电脑自带系统为windows11家庭版&#xff0c;而有些软件无法在家庭版中安装&#xff0c;所以考虑用虚拟机再装个Windows10专业版系统。 前一段时间在Windows11 家庭版上安装的KingSCADA软件运行时总反应很慢&#xff0c;准备…

【MLC】 TensorIR 练习

文章目录 前言TensorIR 练习TensorIR: 张量程序抽象案例研究练习 1&#xff1a;广播加法练习 2&#xff1a;二维卷积练习 3&#xff1a;变换批量矩阵乘法程序 总结 前言 这两天重新看了一下天奇的mlc课程文档&#xff0c;把里边儿的TensorIR 练习写了一下&#xff0c;顺便推广…

ubuntu循环登录,无法进入桌面

现象 在用户登录界面输入用户名和密码后无法正常登录&#xff0c;并且一直循环提示输入登录信息。 问题定位 1. 键入&#xff1a;ctrlaltF1&#xff0c; 进入命令行登录界面 2. 输入当前的用户名和密码&#xff08;也可以是root&#xff0c;操作需谨慎&#xff09; 3.…

【SonarQube】下载、安装、配置、使用介绍

文章目录 SonarQube安装运行使用root启动问题处理修改文件数限制JDK版本问题创建Project创建token扫描代码数据持久化在线文档 SonarQube安装 官网下载地址: http://www.sonarqube.org/downloads/9.9.1.69595下载地址: https://binaries.sonarsource.com/Distribution/sonarqu…

chatgpt赋能python:Python下载之后怎么用:详细教程

Python下载之后怎么用&#xff1a;详细教程 Python作为一种著名的编程语言&#xff0c;已经成为众多程序员和开发者的首选。因此&#xff0c;如果您也想开始使用 Python 来进行编程&#xff0c;那么下一步应该是下载和安装Python。但是&#xff0c;下载完 Python 之后&#xf…

范式迁移 | Squids DBMotion支持Oracle迁移到GaussDB

Squids DBMotion 2304发版成功&#xff0c;再添重量级数据同步功能——支持Oracle迁移到GaussDB。 GaussDB是华为自主创新研发的分布式关系型数据库。该产品具备企业级复杂事务混合负载能力&#xff0c;同时支持分布式事务&#xff0c;同城跨AZ部署&#xff0c;数据0丢失&…

企业使用WordPress网站的6个理由

WordPress 为超过三分之一的网络和超过 38%的顶级 10K 网站提供支持。它最初是一个博客平台&#xff0c;现在是世界上使用最广泛的内容管理系统&#xff0c;对于希望在未来几年扩大规模的网站所有者来说&#xff0c;是一个明智的选择。 除了使用开源软件的好处之外&#xff0c…

从古板到智能:机器程序的华丽转身

因为 ChatGPT 的热潮&#xff0c;目前在恶补人工智能方面的知识。在某一篇文章的评论中&#xff0c;我看到了一个问题&#xff1a;“为什么 ChatGPT 能这么厉害&#xff0c;基本什么问题都能回答&#xff0c;如何做到的” 这也是我想问的问题&#xff0c;在初学编程的时候&…

Packet Tracer - 配置区域策略防火墙

Packet Tracer - 配置区域策略防火墙 拓扑 地址表 设备 接口 IP地址 子网掩码 默认网关 交换机端口 R1 F0/1 192.168.1.1 255.255.255.0 N/A S1 F0/2 S0/3/0 (DCE) 10.1.1.1 255.255.255.252 N/A N/A R2 S0/3/0 10.1.1.2 255.255.255.252 N/A N/A S0/3…

Unity 反射探针

反射射探针 是用来模拟反射周边物体的光照信息的一种解决方案让物体&#xff0c;受周围物体的光照或材质进行影响的一种模拟光照效果。如下图效果&#xff1a; 反射探针属性截图 反射探针类型 Baked 烘焙模式&#xff0c;此种模式需要反射的物体是静态的不能移动&#xff0c;但…

如何高效提问,准确搜索,开发小白不会百度?

How-To-Ask-Question &#xff1f;其实我也是小白&#xff0c;这个问题没有太多发言权。目前来说&#xff0c;我暂时也没有找到一个通法&#xff0c;但整体上来说也不是无迹可寻&#xff08;是有一定技巧和经验在里面的&#xff09;。我之前也经常遇到了一些这方面问题&#x…

1.5. 流程控制(分支与循环)

流程控制是编程中的基本概念&#xff0c;用于控制程序的执行顺序。在 Java 中&#xff0c;流程控制主要分为两类&#xff1a;分支结构&#xff08;Branching&#xff09;和循环结构&#xff08;Looping&#xff09;。 1.5.1. 分支结构 分支结构是根据条件判断来选择执行不同的…

【*1900 DP+Tree】CF9D

Problem - 9D - Codeforces 题意&#xff1a; 思路&#xff1a; 计数问题&#xff0c;考虑计数DP 因为它是二叉树&#xff0c;比较特殊&#xff0c;所以可以考虑一下线性DP 按照题目最后要算的答案&#xff0c;状态可以这样设计&#xff1a; 设dp[i][j]表示树高为i&#x…

Linux设备驱动程序(二)——建立和运行模块

文章目录 前言一、设置测试系统二、Hello World 模块1、代码详解2、执行效果 三、内核模块相比于应用程序1、用户空间和内核空间2、内核的并发3、当前进程4、几个别的细节 四、编译和加载1、编译模块2、加载和卸载模块3、版本依赖 五、内核符号表六、预备知识七、初始化和关停1…