• linux 3.10的list_del


    最近看到一个page的数据比较奇怪:

    crash> page ffffea002c239c58
    struct page {
      flags = 54043195528445952,
      _count = {
        counter = 34-----------------------被引用34次
      },
      {
        _mapcount = {
          counter = -1---------------------没有映射
        },
        {
          inuse = 65535,
          objects = 65535
        }
      },
      {
        {
          private = 0,
          mapping = 0x0------------------为NULL,
        },
        ptl = {
          raw_lock = {
            slock = 0
          }
        },
        slab = 0x0,
        first_page = 0x0
      },
      {
        index = 18446612222659373184,------------index这么大,明显不对
        freelist = 0xffff881508fda880------------转成16进制,这个倒是像对的
      },
      lru = {
        next = 0xdead000000100100,
        prev = 0xdead000000200200
      }
    }

    查看一下这个地址:

    crash> kmem 0xffff881508fda880
    CACHE            NAME                 OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE
    ffff880c17fb0d00 mm_struct               1408        230       580    116     8k
    SLAB              MEMORY            TOTAL  ALLOCATED  FREE
    ffff881508fda280  ffff881508fda300      5          2     3
    FREE / [ALLOCATED]
       ffff881508fda880
    
          PAGE         PHYSICAL      MAPPING       INDEX CNT FLAGS
    ffffea00499f77b0 1508fda000                0        0  1 c0000000000080 slab

    最后看到的是lru的next和prev成员,一开始才疏学浅,觉得这个 0xdead000000100100 和 0xdead000000200200 很奇怪。

    后来搜索之后,才发现这个是将一个entry删除之后,设置的标记值:

    /*
     * Architectures might want to move the poison pointer offset
     * into some well-recognized area such as 0xdead000000000000,
     * that is also not mappable by user-space exploits:
     */
    #ifdef CONFIG_ILLEGAL_POINTER_VALUE
    # define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
    #else
    # define POISON_POINTER_DELTA 0
    #endif
    
    /*
     * These are non-NULL pointers that will result in page faults
     * under normal circumstances, used to verify that nobody uses
     * non-initialized list entries.
     */
    #define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
    #define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)

    删除的代码实现如下:

    static inline void list_del(struct list_head *entry)
    {
        __list_del(entry->prev, entry->next);
        entry->next = LIST_POISON1;
        entry->prev = LIST_POISON2;
    }

    不光光是list,hlist也有类似动作:

    static inline void hlist_del(struct hlist_node *n)
    {
        __hlist_del(n);
        n->next = LIST_POISON1;
        n->pprev = LIST_POISON2;
    }

    在用户态程序中,我们一般通过判断指针是不是是null来判断能否使用这个指针,在将一个entry从list删除后,一般将prev和next设置为NULL,这样做没有什么问题,但是这样就没有

    区分这个entry是从list摘除的,还是本身初始化的,所以为了调试方便,将其设置为一些特殊值,是有意义的。

    当开启调试之后,即开启 CONFIG_DEBUG_LIST,则在加入链表和删除的时候都会加入调试warn

    void __list_del_entry(struct list_head *entry)
    {
        struct list_head *prev, *next;
    
        prev = entry->prev;
        next = entry->next;
    
        if (WARN(next == LIST_POISON1,
            "list_del corruption, %p->next is LIST_POISON1 (%p)
    ",
            entry, LIST_POISON1) ||
            WARN(prev == LIST_POISON2,
            "list_del corruption, %p->prev is LIST_POISON2 (%p)
    ",
            entry, LIST_POISON2) ||
            WARN(prev->next != entry,
            "list_del corruption. prev->next should be %p, "
            "but was %p
    ", entry, prev->next) ||
            WARN(next->prev != entry,
            "list_del corruption. next->prev should be %p, "
            "but was %p
    ", entry, next->prev))
            return;
    
        __list_del(prev, next);
    }
    void __list_add(struct list_head *new,
                      struct list_head *prev,
                      struct list_head *next)
    {
        WARN(next->prev != prev,
            "list_add corruption. next->prev should be "
            "prev (%p), but was %p. (next=%p).
    ",
            prev, next->prev, next);
        WARN(prev->next != next,
            "list_add corruption. prev->next should be "
            "next (%p), but was %p. (prev=%p).
    ",
            next, prev->next, prev);
        WARN(new == prev || new == next,
             "list_add double add: new=%p, prev=%p, next=%p.
    ",
             new, prev, next);
        next->prev = new;
        new->next = next;
        new->prev = prev;
        prev->next = new;
    }

     当然,如果你摘链之后,还要继续挂链的话,还是要初始化的,这个时候就使用:

    static inline void
    list_del_init(struct list_head *entry)
    {
        __list_del(entry->prev, entry->next);
        INIT_LIST_HEAD(entry);-----------------------会重新初始化
    }
    水平有限,如果有错误,请帮忙提醒我。如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。版权所有,需要转发请带上本文源地址,博客一直在更新,欢迎 关注 。
  • 相关阅读:
    JAVA中线程同步的方法(4种)汇总
    java
    指定的元素已经是另一个元素的逻辑子元素。请先将其断开连接。(解决问题总结)
    无法将类型为“System.Windows.Controls.SelectedItemCollection”的对象强制转换为类型“System.Collections.Generic.IList`1
    foreach---集合已修改;可能无法执行枚举操作。
    WPF_View中控件使用单例ViewModel
    判断s2是否能够被通过s1做循环移位(rotate)得到的字符串是否包含
    多列转1列 SqlServer 实现oracle10g的 wmsys.wm_concat()--for xml path('')
    异步对象(XMLHttpRequest)的帮助脚本
    在vs2010使用EF出现CS0012: 类型“System.Data.Objects.DataClasses.EntityObject”在未被引用的程序集中定义
  • 原文地址:https://www.cnblogs.com/10087622blog/p/9625455.html
Copyright © 2020-2023  润新知