ShiroFilterFactoryBean源码及拦截原理深入分析

本篇文章篇幅比较长,但是细看下去相信对学习Shiro应该会有帮助。好了,闲话不多说,直接进入正题:

Shiro提供了与Web集成的支持,其通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,其是安全控制的入口点,其负责读取配置(如ini配置文件),然后判断URL是否需要登录/权限等工作。

而要在Spring中使用Shiro的话,可在web.xml中配置一个DelegatingFilterProxyDelegatingFilterProxy作用是自动到Spring容器查找名字为shiroFilterfilter-name)的bean并把所有Filter的操作委托给它。

首先是在web.xml中配置DelegatingFilterProxy

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

配置好DelegatingFilterProxy后,下面只要再把ShiroFilter配置到Spring容器(此处为Spring的配置文件)即可:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
</bean>
 
 
  • 1
  • 2
  • 3

可以看到我们使用了ShiroFilterFactoryBean来创建shiroFilter,这里用到了Spring中一种特殊的Bean——FactoryBean。当需要得到名为”shiroFilter“的bean时,会调用其getObject()来获取实例。下面我们通过分析ShiroFilterFactoryBean创建实例的过程来探究Shiro是如何实现安全拦截的:

    public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中调用了createInstance()来创建实例:

  protected AbstractShiroFilter createInstance() throws Exception {

        // 这里是通过FactoryBean注入的SecurityManager(必须)
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }

        FilterChainManager manager = createFilterChainManager();

        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

可以看到创建SpringShiroFilter时用到了两个组件:SecurityManagerChainResolver

  • SecurityManager:我们知道其在Shiro中的地位,类似于一个“安全大管家”,相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher,是Shiro的心脏,所有具体的交互都通过SecurityManager进行控制,它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
  • ChainResolver:Filter链解析器,用来解析出该次请求需要执行的Filter链。
  • PathMatchingFilterChainResolverChainResolver的实现类,其中还包含了两个重要组件FilterChainManagerPatternMatcher
  • FilterChainManager:管理着Filter和Filter链,配合PathMatchingFilterChainResolver解析出Filter链
  • PatternMatcher:用来进行请求路径匹配,默认为Ant风格的路径匹配

先有一个大体的了解,那么对于源码分析会有不少帮助。下面会对以上两个重要的组件进行分析,包括PathMatchingFilterChainResolverFilterChainManager。首先贴一段ShiroFilter的在配置文件中的定义:

<!-- Shiro的Web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="unauthorizedUrl" value="/special/unauthorized" />
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter" />
                <entry key="logout" value-ref="logoutFilter" />
                <entry key="ssl" value-ref="sslFilter"></entry>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /resources/** = anon
                /plugin/** = anon
                /download/** = anon
                /special/unauthorized = anon
                /register = anon
                /login = ssl,authc
                /logout = logout
                /admin/** = roles[admin]

                /** = user
            </value>
        </property>
    </bean>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

再来看看PathMatchingFilterChainResolverFilterChainManager的创建过程:

  protected FilterChainManager createFilterChainManager() {

        // 默认使用的FilterChainManager是DefaultFilterChainManager
        DefaultFilterChainManager manager = new DefaultFilterChainManager();
        // DefaultFilterChainManager默认会注册的filters(后面会列出)
        Map<String, Filter> defaultFilters = manager.getFilters();

        // 将ShiroFilterFactoryBean配置的一些公共属性(上面配置的loginUrl,successUrl,unauthorizeUrl)应用到默认注册的filter上去
        for (Filter filter : defaultFilters.values()) {
            applyGlobalPropertiesIfNecessary(filter);
        }

        // 处理自定义的filter(上面配置的filters属性),步骤类似上面
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();
                Filter filter = entry.getValue();
                applyGlobalPropertiesIfNecessary(filter);
                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                // 将Filter添加到manager中去,可以看到对于Filter的管理是依赖于FilterChainManager的
                manager.addFilter(name, filter, false);
            }
        }

        // 根据FilterChainDefinition的配置来构建Filter链(上面配置的filterChainDefinitions属性)
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                // 后面会分析该步的源码,功能上就是创建Filter链
                manager.createChain(url, chainDefinition);
            }
        }

        return manager;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

下面有必要来看看DefaultFilterChainManager的源码,分析一下上面调用到的方法。先来看看他的几个重要的属性:

    private FilterConfig filterConfig;

    private Map<String, Filter> filters; //pool of filters available for creating chains

    private Map<String, NamedFilterList> filterChains; //key: chain name, value: chain
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

其中filterConfig仅在初始化Filter时有效,而我们自定义的Filter都不是init的,所以该属性可以暂时忽略()。
而后面两张map就重要了:filters中缓存了所有添加的filter,filterChains则缓存了所有的filterChain。其中前者的key是filter name,value是Filter。而后者的key是chain name,value是NamedFilterList
有的童鞋可能会问NamedFilterList是怎么样的结构呢,你可以把它当成List<Filter>,这样就好理解了吧。下面再分析刚才createFilterChainManager()中调用过的manager的几个方法:

  • addFilter(缓存filter让manager来管理)
    public void addFilter(String name, Filter filter, boolean init) {
        addFilter(name, filter, init, true);
    }

    protected void addFilter(String name, Filter filter, boolean init, boolean overwrite) {
        Filter existing = getFilter(name);
        if (existing == null || overwrite) {
            if (filter instanceof Nameable) {
                ((Nameable) filter).setName(name);
            }
            if (init) {
                initFilter(filter);
            }
            this.filters.put(name, filter);
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

filter缓存到filters这张map里,不管是默认注册的还是自定义的都需要FilterChainManager来统一管理。

  • createChain:创建filterChain并将定义的filter都加进去
    // chainName就是拦截路径"/resources/**",chainDefinition就是多个过滤器名的字符串
    public void createChain(String chainName, String chainDefinition) {
        if (!StringUtils.hasText(chainName)) {
            throw new NullPointerException("chainName cannot be null or empty.");
        }
        if (!StringUtils.hasText(chainDefinition)) {
            throw new NullPointerException("chainDefinition cannot be null or empty.");
        }

        if (log.isDebugEnabled()) {
            log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");
        }

        // 先分离出配置的各个filter,比如 
        // "authc, roles[admin,user], perms[file:edit]" 分离后的结果是:
        // { "authc", "roles[admin,user]", "perms[file:edit]" }
        String[] filterTokens = splitChainDefinition(chainDefinition);

        // 进一步分离出"[]"内的内容,其中nameConfigPair是一个长度为2的数组
        // 比如 roles[admin,user] 经过解析后的nameConfigPair 为{"roles", "admin,user"}
        for (String token : filterTokens) {
            String[] nameConfigPair = toNameConfigPair(token);

            // 得到了 拦截路径、filter以及可能的"[]"中的值,那么执行addToChain
            addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • addToChain
   public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
        if (!StringUtils.hasText(chainName)) {
            throw new IllegalArgumentException("chainName cannot be null or empty.");
        }
        Filter filter = getFilter(filterName);
        if (filter == null) {
            throw new IllegalArgumentException("There is no filter with name '" + filterName + "' to apply to chain [" + chainName + "] in the pool of available Filters.  Ensure a " + "filter with that name/path has first been registered with the addFilter method(s).");
        }

        // 将匹配关系注册到filter中,后续会提到
        applyChainConfig(chainName, filter, chainSpecificFilterConfig);

        // 确保chain已经被加到filterChains这张map中了
        NamedFilterList chain = ensureChain(chainName);
        // 将该filter加入当前chain
        chain.add(filter);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

至此,FilterChainManager就创建完了,它无非就是缓存了两张map,没有什么逻辑上的操作。下面将FilterChainManager设置到PathMatchingFilterChainResolver中。PathMatchingFilterChainResolver实现了FilterChainResolver接口,该接口中只定义了一个方法:

FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);
 
 
  • 1

通过解析请求来得到一个新的FilterChain。而PathMatchingFilterChainResolver实现了该接口,依靠了FilterChainManager中保存的chainFiltersfilters这两张map来根据请求路径解析出相应的filterChain,并且和originalChain组合起来使用。下面具体看看PathMatchingFilterChainResolver中的实现:

   public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        // 得到 FilterChainManager 
        FilterChainManager filterChainManager = getFilterChainManager();
        if (!filterChainManager.hasChains()) {
            return null;
        }

        String requestURI = getPathWithinApplication(request);

        // chainNames就是刚定义的filterChains的keySet,也就是所有的路径集合(比如:["/resources/**","/login"])
        for (String pathPattern : filterChainManager.getChainNames()) {

            // 请求路径是否匹配某个 定义好的路径:
            if (pathMatches(pathPattern, requestURI)) {
                if (log.isTraceEnabled()) {
                    log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " + "Utilizing corresponding filter chain...");
                }
                // 找到第一个匹配的Filter链,那么就返回一个ProxiedFilterChain
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }

        return null;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这里返回只有两种情况,要么是null,要么就是一个ProxiedFilterChain。返回null并不表示中断FilterChain,而是只用originChain。而关于ProxiedFilterChain,它实现了FilterChain,内部维护了两份FilterChain(其实一个是FilterChain,另一个是List<Filter>
FilterChain也就是web.xml中注册的Filter形成的FilterChain,我们称之为originChain。而另一个List<Filter>则是我们在Shiro中注册的Filter链了,下面看看ProxiedFilterChain中关于doFilter(...)的实现:

 public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.filters == null || this.filters.size() == this.index) {
            //we've reached the end of the wrapped chain, so invoke the original one:
            if (log.isTraceEnabled()) {
                log.trace("Invoking original filter chain.");
            }
            this.orig.doFilter(request, response);
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Invoking wrapped filter at index [" + this.index + "]");
            }
            this.filters.get(this.index++).doFilter(request, response, this);
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以看到,它会先执行Shiro中执行的filter,然后再执行web.xml中的Filter。不过要注意的是,需要等到originChain执行到ShiroFilter之后才会执行Shiro中的Filter链。
至此,两个组件的创建过程差不多都介绍完了,那么当这两个组件创建完毕后,是如何工作的呢?
先从ShiroFilter入手,因为它是总的拦截器,看看其中的doFilterInternal(...)方法:

  protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {

        Throwable t = null;

        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            final Subject subject = createSubject(request, response);

            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    // 其实需要关心的就在这里
                    // touch一下session
                    updateSessionLastAccessTime(request, response);
                    // 执行Filter链
                    executeChain(request, response, chain);
                    return null;
                }
            });
        } catch (ExecutionException ex) {
            t = ex.getCause();
        } catch (Throwable throwable) {
            t = throwable;
        }

        if (t != null) {
            if (t instanceof ServletException) {
                throw (ServletException) t;
            }
            if (t instanceof IOException) {
                throw (IOException) t;
            }
            //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
            String msg = "Filtered request failed.";
            throw new ServletException(msg, t);
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

跟进executeChain(...)方法:

    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        FilterChain chain = getExecutionChain(request, response, origChain);
        chain.doFilter(request, response);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

如何得到FilterChain的呢?如果你认真的看到这里,那么你应该不难想到其中肯定利用了刚才注册的ChainResolver

   protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;

        FilterChainResolver resolver = getFilterChainResolver();
        if (resolver == null) {
            log.debug("No FilterChainResolver configured.  Returning original FilterChain.");
            return origChain;
        }

        FilterChain resolved = resolver.getChain(request, response, origChain);
        if (resolved != null) {
            log.trace("Resolved a configured FilterChain for the current request.");
            chain = resolved;
        } else {
            log.trace("No FilterChain configured for the current request.  Using the default.");
        }

        return chain;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

猜对了~并且也验证了当resolver.getChain(...)返回null时,直接使用originChain了。然后执行返回的FilterChaindoFilter(...)方法。这个过程我们再脱离代码来分析一下:当我们从浏览器发出一个请求,究竟发生了什么?
这里只站在Filter的层面来分析。服务器启动后,读取web.xml中的filterfilter-mapping节点后组成FilterChain,对请求进行拦截。拦截的顺序按照filter节点的定义顺序,Shiro利用ShiroFilter来充当一个总的拦截器来分发所有需要被Shiro拦截的请求,所以我们看到在Shiro中我们还可以自定义拦截器。ShiroFilter根据它在拦截器中的位置,只要执行到了那么就会暂时中断原FilterChain的执行,先执行Shiro中定义的Filter,最后再执行原FilterChian。可以打个比方,比如说本来有一条铁链,一直蚂蚁从铁链的开端往末端爬,其中某一环叫ShiroFilter,那么当蚂蚁爬到ShiroFilter这一环时,将铁链打断,并且接上另一端铁链(Shiro中自定义的Filter),这样就构成了一条新的铁链。然后蚂蚁继续爬行(后续的执行过程)。

到这里,我们已经根据请求路径找到了一条Filter链(originChain + shiroChain),之后就是对链上的FilterdoFilter,其中关于如何
就是filter后配置的[]部分是如何生效的,我们可以看PathMatchingFilter中的Prehandle(...)方法:

    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
            if (log.isTraceEnabled()) {
                log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");
            }
            return true;
        }

        // appliedPaths中保存了该filter中能拦截的路径和该路径配置的key-value对,比如{key="/admin/**", value="[admin]"}
        for (String path : this.appliedPaths.keySet()) {
            // 首先是匹配路径
            if (pathsMatch(path, request)) {
                log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);
                // 然后开始验证“[]”中的字符串
                Object config = this.appliedPaths.get(path);
                return isFilterChainContinued(request, response, path, config);
            }
        }

        //no path matched, allow the request to go through:
        return true;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

下面跟踪isFilterChainContinued(...)

 private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
                                           String path, Object pathConfig) throws Exception {

        if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
            if (log.isTraceEnabled()) {
                 // log
            }
            return onPreHandle(request, response, pathConfig);
        }

        if (log.isTraceEnabled()) {
            // log
        }
        return true;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

基本也就是交给onPreHandle(...)来处理,所以一般需要验证”[]“中字符串的filter都会扩展这个方法,比如AccessControlFilter

    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
    }
 
 
  • 1
  • 2
  • 3

RolesAuthorizationFilter中:

    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {

        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;

        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return true;
        }

        Set<String> roles = CollectionUtils.asSet(rolesArray);
        return subject.hasAllRoles(roles);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

最后附上默认注册的filters

public enum DefaultFilter {

    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

水平有限,写得蛮不容易,看源码加写花了整整2天。希望对大家能有帮助~

  •                     <li class="tool-item tool-active is-like  tool-clicked"><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#csdnc-thumbsup"></use>
                        </svg><span class="name">点赞</span>
                        <span class="count">33</span>
                        </a></li>
                        <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;popu_824&quot;}"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-Collection-G"></use>
                        </svg><span class="name">收藏</span></a></li>
                        <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-fenxiang"></use>
                        </svg>分享</a></li>
                        <!--打赏开始-->
                                                <!--打赏结束-->
                                                <li class="tool-item tool-more">
                            <a>
                            <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
                            </a>
                            <ul class="more-box">
                                <li class="item"><a class="article-report">文章举报</a></li>
                            </ul>
                        </li>
                                            </ul>
                </div>
                            </div>
            <div class="person-messagebox">
                <div class="left-message"><a href="https://blog.csdn.net/u012345283">
                    <img src="https://profile.csdnimg.cn/7/4/6/3_u012345283" class="avatar_pic" username="u012345283">
                                            <img src="https://g.csdnimg.cn/static/user-reg-year/1x/6.png" class="user-years">
                                    </a></div>
                <div class="middle-message">
                                        <div class="title"><span class="tit"><a href="https://blog.csdn.net/u012345283" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}" target="_blank">百里马</a></span>
                                            </div>
                    <div class="text"><span>发布了211 篇原创文章</span> · <span>获赞 79</span> · <span>访问量 76万+</span></div>
                </div>
                                <div class="right-message">
                                            <a href="https://bbs.csdn.net/forums/p-u012345283" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-messageboard">他的留言板
                        </a>
                                                            <a class="btn btn-sm  bt-button personal-watch" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}">关注</a>
                                    </div>
                            </div>
                    </div>
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 《深入分析Linux内核源码 陈莉君pdf》是一本介绍Linux内核源码分析的教材。本书适合有一定编程基础和Linux操作系统了解的读者,帮助读者深入了解Linux内核的原理和实现。 这本书主要分为两个部分:前半部分是对Linux内核的概述和基础知识的介绍,后半部分则深入分析Linux内核的各个模块和功能。 在前半部分,作者介绍了Linux的发展历史、内核架构、进程管理、内存管理、文件系统等基础知识。通过这些基础知识的学习,读者可以对Linux内核的整体结构和运行原理有一个全面的了解。 后半部分则深入分析了Linux内核的各个模块和功能,如进程调度、内存管理、文件系统、网络协议栈等。作者通过阐述源码分析方法和示例代码,帮助读者理解和掌握Linux内核的具体实现。这些模块和功能的深入分析,可以帮助读者更好地理解Linux内核的工作原理,提升对系统的调试和优化能力。 总体而言,这本书对于想要深入了解Linux内核的读者来说非常有价值。通过分析内核源码,读者可以更好地理解Linux操作系统的底层原理和工作机制,从而对系统的运行和调试有深入的了解。但需要指出的是,由于Linux内核的复杂性,读者需要具备一定的编程和操作系统基础,以更好地理解书中的内容。这本书是一本较为深入的技术书籍,需要读者花一些时间和精力进行学习和实践。 ### 回答2: 《深入分析Linux内核源码 陈莉君pdf》是一本深入分析Linux内核源码的书籍,作者陈莉君是一位资深的Linux内核工程师。本书主要分为三个部分:内核原理、内核源码分析和内核调试技术。 首先,本书介绍了Linux内核的基本原理,包括进程管理、内存管理、文件系统、设备驱动等核心模块的实现原理。通过对这些原理的深入解析,读者可以更加全面地了解Linux内核的运作机制。 其次,本书详细解读了Linux内核的源码结构和各个模块的实现细节。作者通过逐行分析和注释,揭示了内核代码的设计思路和实现细节,让读者可以更加深入地理解内核源码的结构和逻辑。 最后,本书介绍了一些内核调试技术,包括使用gdb调试内核、使用内核跟踪工具分析内核性能等。这些技术可以帮助读者解决实际的内核问题,提高调试和优化内核的能力。 本书的特点是通俗易懂、深入浅出。即使读者没有过多的编程和操作系统基础,通过仔细阅读,也能够理解其中的内容。此外,本书还提供了大量的源码示例和实践案例,读者可以通过阅读和实践来加深对Linux内核的理解。 总而言之,《深入分析Linux内核源码 陈莉君pdf》是一本非常实用和权威的Linux内核书籍,对于需要深入了解Linux内核工作原理和如何分析和调试内核的开发人员和研究人员来说是一本宝贵的参考资料。 ### 回答3: 《深入分析Linux内核源码》是陈莉君编写的一本关于Linux内核源码分析的书籍。该书通过对Linux内核源码的解读和分析,帮助读者深入了解Linux内核的工作原理和设计思想。 首先,该书介绍了Linux内核的架构和基本组成,包括进程管理、内存管理、文件系统、设备驱动、网络和进程间通信等核心部分。通过对这些组件的详细解析,读者可以逐步建立起对Linux内核整体架构的认识。 其次,该书通过具体的代码实例,详细解释了Linux内核的关键数据结构和算法。例如,书中详细介绍了进程调度算法和内存管理机制的实现原理,读者可以通过代码级别的分析深入理解其工作原理和性能优化方法。 此外,该书还介绍了Linux内核的调试和性能分析工具,如GDB、SystemTap等。这些工具可以帮助读者在分析Linux内核源码时进行实时调试和性能优化,提高代码的可读性和性能。 总的来说,陈莉君的《深入分析Linux内核源码》是一本深入浅出、通俗易懂的Linux内核源码解析书籍。通过阅读该书,读者可以系统地学习和理解Linux内核的设计思想和实现方法,对Linux内核的开发和调试有更深入的认识和理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值