上一篇<<baddy:初始化内存域>>分析了内存域的初始化过程,及扩展内容cpu热插拔函数的注册及热插拔线程的作用等等。UMA下只有一个pglist_data对象也就是对应一个内存域,而NUMA下最多拥有5个内存域(zone), ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE等等,内存域类型根据具体硬件来决定。

baddy系统的入口(核心)函数为__alloc_pages,经典调用可以追溯到slub分配器的kmalloc函数,参考<<slub构建过程>>。

__alloc_pages

__alloc_pages函数实现在mm/page_alloc.c文件:

struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid,nodemask_t *nodemask)
{struct page *page;unsigned int alloc_flags = ALLOC_WMARK_LOW; // 默认低水位线// #define ALLOC_WMARK_MIN         WMARK_MIN       // 最低水位线// #define ALLOC_WMARK_LOW         WMARK_LOW        // 低水位线// #define ALLOC_WMARK_HIGH        WMARK_HIGH        // 高水位线// #define ALLOC_NO_WATERMARKS     0x04              // 不检查水位线// enum zone_watermarks {//        WMARK_MIN,              // 内存区域的空闲页面最低水位线,说明本区域的内存严重不足//        WMARK_LOW,               // 内存区域的空闲页面低水位线,说明本区域的内存轻微不足,默认值为WMARK_MIN的125%//        WMARK_HIGH,             // 内存区域的空闲页数高水位线,说明本区域内存充足,默认值为WMARK_MAX的150%//        WMARK_PROMO,           // 内存区域的空闲页面大于高水位线;当内存需要回收时,对快速内存节点通过kswapd以回收页面,至空闲页面略高于WMARK_PROMO水位线//        NR_WMARK                  // unsigned long _watermark[NR_WMARK]; 表示区域(zone)水平线,由WMARK_MIN... WMARK_PROMO索引// };gfp_t alloc_gfp; /* The gfp_t that was actually used for allocation */struct alloc_context ac = { };/** There are several places where we assume that the order value is sane* so bail out early if the request is out of bound.*/if (WARN_ON_ONCE_GFP(order >= MAX_ORDER, gfp)) // 调用dump_stack,打印堆栈信息,指定__GFP_NOWARN标志时,不输出警告信息return NULL;gfp &= gfp_allowed_mask;// 在早期引导期间,gfp_allowed_mask被设置为GFP_BOOT_MASK,// 以限制在启用中断之前使用的GFP标志// 一旦启用了中断,在系统运行时将其设置为__GFP_BITS_MASK// 在休眠期间,PM使用它来避免设备挂起时内存分配期间的I/O/* 应用作用域分配约束。这主要是关于GFP_NOFS的。GFP_NOIO必须从一个特定的上下文继承所有的分配请求,该上下文被标记为memalloc_no{fs,io}_{save,restore}。PF_MEMALLOC_PIN确保在分配时不使用可移动区域 */gfp = current_gfp_context(gfp);alloc_gfp = gfp;if (!prepare_alloc_pages(gfp, order, preferred_nid, nodemask, &ac,&alloc_gfp, &alloc_flags))return NULL;

进入prepare_alloc_pages函数:

static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,int preferred_nid, nodemask_t *nodemask,struct alloc_context *ac, gfp_t *alloc_gfp,unsigned int *alloc_flags)
{ac->highest_zoneidx = gfp_zone(gfp_mask);  // highest_zoneidx表示分配请求的最高可用区域索引// 由于分区的性质,低于highest_zoneidx的分区上的内存将受到lowmem_reserve[highest_zoneidx]的保护// 回收/压缩也使用最高的zoneidx来限制目标区域// 因为高于此索引的区域不能用于此分配请求ac->zonelist = node_zonelist(preferred_nid, gfp_mask); // 获取区域列表ac->nodemask = nodemask;ac->migratetype = gfp_migratetype(gfp_mask); // 将GFP标志转换为其相应的迁移类型if (cpusets_enabled()) {  // cpusets有效*alloc_gfp |= __GFP_HARDWALL; // __GFP_HARDWALL 强制执行cpuset内存分配策略/** When we are in the interrupt context, it is irrelevant* to the current task context. It means that any node ok.*/if (in_task() && !ac->nodemask) // 在任务上下文中并且nodemask为NULLac->nodemask = &cpuset_current_mems_allowed;// #define cpuset_current_mems_allowed (current->mems_allowed)else*alloc_flags |= ALLOC_CPUSET;  // 检查cpuset是否正确}fs_reclaim_acquire(gfp_mask); // 检查(符合)/增加页面回收独占锁fs_reclaim_release(gfp_mask); // 释放锁might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM); // 可以回收的情况下,主动触发一次调度if (should_fail_alloc_page(gfp_mask, order)) //  should_fail_alloc_page做一些预检查, 一些无法分配的条件会直接报错return false;*alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, *alloc_flags); // 允许来自CMA区域的分配 ?ac->spread_dirty_pages = (gfp_mask & __GFP_WRITE);// __GFP_WRITE表示调用者允许获取脏页面// 在可能的情况下,这些页面将分布在本地区域之间// 以避免所有脏页面位于一个区域中(公平区域分配策略)ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,ac->highest_zoneidx, ac->nodemask); // 首选区域用于统计,它也用作zonelist迭代器的起点// 返回位于或低于允许节点掩码内给定区域索引的第一个区域return true;
}

继续看__alloc_pages函数:

alloc_flags |= alloc_flags_nofragment(ac.preferred_zoneref->zone, gfp); // 计算分配标志page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac); // 从足够空闲的区域中分配页
||
\/
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,const struct alloc_context *ac)
{...no_fallback = alloc_flags & ALLOC_NOFRAGMENT;z = ac->preferred_zoneref;for_next_zone_zonelist_nodemask(zone, z, ac->highest_zoneidx,ac->nodemask) { // 扫描区域列表,寻找一个有足够空闲的区域if (cpusets_enabled() &&(alloc_flags & ALLOC_CPUSET) &&!__cpuset_zone_allowed(zone, gfp_mask))// cpusets有效并且已设置ALLOC_CPUSET标志,在符合条件的区域中寻找continue;if (ac->spread_dirty_pages) { // 允许获取脏页面if (last_pgdat != zone->zone_pgdat) { 如果区域的zone_pgdat节点不等于 last_pgdat节点last_pgdat = zone->zone_pgdat; // 赋给last_pgdatlast_pgdat_dirty_ok = node_dirty_ok(zone->zone_pgdat);}if (!last_pgdat_dirty_ok)continue;}if (no_fallback && nr_online_nodes > 1 &&zone != ac->preferred_zoneref->zone) {// 避免混合页面块类型 并且 NUMA节点数量大于1 并且 当前遍历到的区域不是起点区域int local_nid;/** If moving to a remote node, retry but allow* fragmenting fallbacks. Locality is more important* than fragmentation avoidance.*/local_nid = zone_to_nid(ac->preferred_zoneref->zone);if (zone_to_nid(zone) != local_nid) { // 如果不是本地节点alloc_flags &= ~ALLOC_NOFRAGMENT; // 取消ALLOC_NOFRAGMENT标志goto retry;}}mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK);  // 如果分配标志为低水位线(默认值)if (!zone_watermark_fast(zone, order, mark,ac->highest_zoneidx, alloc_flags,gfp_mask)) { // 区域水位线是否符合分配int ret;...ret = node_reclaim(zone->zone_pgdat, gfp_mask, order); // 回收未映射(使用)的文件备份页面switch (ret) {case NODE_RECLAIM_NOSCAN:/* did not scan */continue;case NODE_RECLAIM_FULL:/* scanned but unreclaimable */continue;default:/* did we reclaim enough */if (zone_watermark_ok(zone, order, mark,ac->highest_zoneidx, alloc_flags)) // 当前内存区中的页面是否在水位线以上goto try_this_zone;continue;}}try_this_zone:     page = rmqueue(ac->preferred_zoneref->zone, zone, order,gfp_mask, alloc_flags, ac->migratetype); // 迁移到指定类型,合并成连续的页,唤醒kswapd

rmqueue函数分析,继续看__alloc_pages函数:

             if (page) {prep_new_page(page, order, gfp_mask, alloc_flags); // 准备分配页,lru.next = 1 ?0/** If this is a high-order atomic allocation then check* if the pageblock should be reserved for the future*/if (unlikely(order && (alloc_flags & ALLOC_HARDER)))reserve_highatomic_pageblock(page, zone, order); // 保留pageblock以独占使用高阶原子分配return page;}...if (no_fallback) {alloc_flags &= ~ALLOC_NOFRAGMENT; // 去除避免混合页面块类型标志goto retry;}return NULL;
}

继续看__alloc_pages函数:

alloc_gfp = gfp;
ac.spread_dirty_pages = false;
ac.nodemask = nodemask;
page = __alloc_pages_slowpath(alloc_gfp, order, &ac);

__alloc_pages_slowpath函数分析,继续看__alloc_pages函数:

out:if (memcg_kmem_enabled() && (gfp & __GFP_ACCOUNT) && page &&unlikely(__memcg_kmem_charge_page(page, gfp, order) != 0)) {// mem_cgroup开启 并且 页分配在mem_cgroup 并且 页有效 并且 mem_cgroup不能控制页__free_pages(page, order); // 释放页page = NULL;}trace_mm_page_alloc(page, order, alloc_gfp, ac.migratetype); // 增加trace打印页相关信息return page;
}
EXPORT_SYMBOL(__alloc_pages);

到这里页分配核心函数流程分析完成了,  查看trace_mm_page_alloc函数分析

rmqueue

static inline
struct page *rmqueue(struct zone *preferred_zone,struct zone *zone, unsigned int order,gfp_t gfp_flags, unsigned int alloc_flags,int migratetype)
{unsigned long flags;struct page *page;if (likely(pcp_allowed_order(order))) { // 检查是否符合order标准,普通页order <= 3,32KB或以内// 大页order <= 9,2MB或以内if (!IS_ENABLED(CONFIG_CMA) || alloc_flags & ALLOC_CMA ||migratetype != MIGRATE_MOVABLE) { // 如果支持CMA内存分配器(分配大块连续物理内存)page = rmqueue_pcplist(preferred_zone, zone, order,gfp_flags, migratetype, alloc_flags);// 从每cpu列表中取出page(释放lru)goto out;}}/* 我们绝对不希望调用方使用__GFP_NOFAIL分配大于order-1的页面单元 */WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));do {page = NULL;spin_lock_irqsave(&zone->lock, flags);/* 当由于非cma分配上下文跳过pcplist时,order-0请求可以到达这里。*//* HIGHATOMIC区域是为高阶原子分配保留的,因此order-0请求应该跳过它 */if (order > 0 && alloc_flags & ALLOC_HARDER)page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);// 遍历指定的migratetype的空闲列表,并从空闲列表中删除最小的可用页面 zone->free_area[order].nr_free--;// 合并到连续的页面  free_area->free_listif (!page) {page = __rmqueue(zone, order, migratetype, alloc_flags);// 合并到连续的(CMA)页面  if (!page)goto failed;}__mod_zone_freepage_state(zone, -(1 << order),get_pcppage_migratetype(page)); // 修改页状态spin_unlock_irqrestore(&zone->lock, flags);} while (check_new_pages(page, order));__count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order); // 更新每cpu状态zone_statistics(preferred_zone, zone, 1); // 更新当前zone的统计信息out:/* Separate test+clear to avoid unnecessary atomics */if (test_bit(ZONE_BOOSTED_WATERMARK, &zone->flags)) {clear_bit(ZONE_BOOSTED_WATERMARK, &zone->flags); // 清除ZONE_BOOSTED_WATERMARK标志wakeup_kswapd(zone, 0, 0, zone_idx(zone)); // 唤醒kswapd// 如果区域具有由伙伴分配器管理的页面,则返回true// 所有回收决策都必须使用此函数}VM_BUG_ON_PAGE(page && bad_range(zone, page), page);return page;failed:spin_unlock_irqrestore(&zone->lock, flags);return NULL;
}

__alloc_pages_slowpath

static inline struct page *
__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,struct alloc_context *ac){bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;const bool costly_order = order > PAGE_ALLOC_COSTLY_ORDER;...if (WARN_ON_ONCE((gfp_mask & (__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)) ==(__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)))// 如果gfp_mask包含__GFP_ATOMIC和__GFP_DIRECT_RECLAIM标志,输出一次警告// %__GFP_ATOMIC表示调用者不能回收或休眠// %__GFP_DIRECT_RECLAIM表示调用者可以直接回收gfp_mask &= ~__GFP_ATOMIC; // 去除__GFP_ATOMIC标志.../* 快速路径仅在需要唤醒kswapd之前使用alloc_flags */alloc_flags = gfp_to_alloc_flags(gfp_mask);/* 我们需要重新计算区域列表迭代器的起点,因为我们可能在快速路径中使用了不同的节点掩码,或者有一个cpuset修改,我们正在重试,否则我们可能会在不合格的区域上无休止地迭代 */ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,ac->highest_zoneidx, ac->nodemask);// 首选区域用于统计,它也用作zonelist迭代器的起点// 返回位于或低于允许节点掩码内给定区域索引的第一个区域if (!ac->preferred_zoneref->zone)goto nopage;...if (alloc_flags & ALLOC_KSWAPD) wake_all_kswapds(order, gfp_mask, ac); // 唤醒所有kswapd线程page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac); // 从足够空闲的区域中分配页if (page)goto got_pg;/* 对于开销较大的分配,请先尝试直接压缩,因为很可能我们有足够的基页,不需要回收。对于不可移动的高顺序分配,也要这样做,因为压缩将尝试通过从相同migratetype的块迁移来防止永久碎片。对于允许忽略水印的分配,不要尝试这样做,因为还没有试过alloc_no_watermark(可以测试一下) */if (can_direct_reclaim &&(costly_order ||(order > 0 && ac->migratetype != MIGRATE_MOVABLE))&& !gfp_pfmemalloc_allowed(gfp_mask)) {page = __alloc_pages_direct_compact(gfp_mask, order,alloc_flags, ac,INIT_COMPACT_PRIORITY,&compact_result);  // 在回收之前,尝试对高阶分配进行内存压缩       if (page)goto got_pg;/* 使用__GFP_NORETRY检查昂贵的分配,其中包括一些THP页面错误分配 */                       if (costly_order && (gfp_mask & __GFP_NORETRY)) {if (compact_result == COMPACT_SKIPPED ||compact_result == COMPACT_DEFERRED) // 内存无法压缩或压缩失败goto nopage;/* 看起来回收/压缩值得一试,但同步压缩可能非常昂贵,所以请继续使用异步压缩 */compact_priority = INIT_COMPACT_PRIORITY;}}
retry:/* Ensure kswapd doesn't accidentally go to sleep as long as we loop */if (alloc_flags & ALLOC_KSWAPD)wake_all_kswapds(order, gfp_mask, ac); // 唤醒所有kswapd线程reserve_flags = __gfp_pfmemalloc_flags(gfp_mask);// 区分真正需要访问全部内存储备的请求和可以接受部分内存的请求if (reserve_flags)alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, reserve_flags);  // 允许来自CMA区域的分配 ?/* 如果可以忽略内存策略,请重置nodemask和zonelist迭代器这些分配具有高优先级和系统性,而不是面向用户 */if (!(alloc_flags & ALLOC_CPUSET) || reserve_flags) {ac->nodemask = NULL;ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,ac->highest_zoneidx, ac->nodemask);}/* 尝试使用可能已经调整的区域列表和alloc_flags */page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);if (page)goto got_pg;/* 调用者不愿意回收页(页标识没有设置),我们无法平衡任何事情if (!can_direct_reclaim)goto nopage;/* 避免递归回收 */if (current->flags & PF_MEMALLOC)goto nopage;/* 尝试直接压缩,然后分配 */page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac,&did_some_progress);if (page)goto got_pg;// __GFP_NORETRY 尝试轻量级的回收,避免循环,避免oom killer那样直接破坏if (gfp_mask & __GFP_NORETRY)goto nopage;// 如果有迹象表明在其他地方取得了进展,// 虚拟机实现将重试以前失败的内存回收过程// 它可以等待其他任务尝试高级方法来释放内存,例如压缩(消除碎片)和分页if (costly_order && !(gfp_mask & __GFP_RETRY_MAYFAIL))goto nopage;// 检查重试回收以向前推进给定的分配请求是否有意义// 如果我们连续尝试MAX_RECLAIM_RETRIES次重试均未成功,// 或者如果我们回收LRU列表上的所有剩余页面,// 甚至无法满足水印要求,我们就会放弃if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,did_some_progress > 0, &no_progress_loops))goto retry;// 如果order-0回收无法取得任何进展,// 则重试压缩没有任何意义,因为压缩的当前实现取决于足够的可用内存量if (did_some_progress > 0 &&should_compact_retry(ac, order, alloc_flags,compact_result, &compact_priority,&compaction_retries))goto retry;// 在我们开始OOM杀死之前,处理可能的cpuset更新竞争if (check_retry_cpuset(cpuset_mems_cookie, ac))goto retry_cpuset;// 回收失败,开始oom killerpage = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress);if (page)goto got_pg;// 避免无水线的分配无限循环if (tsk_is_oom_victim(current) &&(alloc_flags & ALLOC_OOM ||(gfp_mask & __GFP_NOMEMALLOC)))// 当前线程被oom killer ?// oom 异步回收// __GFP_NOMEMALLOC被用来明确禁止进入紧急储备goto nopage;/* 只要oom 有进展,就重试 */if (did_some_progress) {no_progress_loops = 0;goto retry;}nopage:/* 在失败之前处理可能的cpuset更新竞争 */if (check_retry_cpuset(cpuset_mems_cookie, ac))goto retry_cpuset;/* 确保__GFP_NOFAIL请求不会泄漏,并确保我们总是重试 */if (gfp_mask & __GFP_NOFAIL) {/ * __GFP_NOFAIL的所有现有用户都是可阻塞的,因此如果有任何新用户实际需要GFP_NOWAIT,就会发出警告 */if (WARN_ON_ONCE_GFP(!can_direct_reclaim, gfp_mask))goto fail;/* 来自这个上下文的PF_MEMALLOC请求是相当奇怪的,因为我们不能回收任何东西,只能循环等待某人(线程)为我们做一个工作 */WARN_ON_ONCE_GFP(current->flags & PF_MEMALLOC, gfp_mask);/* 不失败的昂贵orders是一项艰巨的要求,我们对此没有太多准备,因此让我们警告这些用户,以便我们能够识别他们并将其转换为其他产品 */WARN_ON_ONCE_GFP(order > PAGE_ALLOC_COSTLY_ORDER, gfp_mask);/* 通过允许它们访问内存储备来帮助非失败的分配,但不要使用ALLOC_NO_WATERMARKS,因为这可能会耗尽整个内存储备,这只会使情况变得更糟 */page = __alloc_pages_cpuset_fallback(gfp_mask, order, ALLOC_HARDER, ac);if (page)goto got_pg;// 主动放权,等待下一次的调度运行cond_resched();goto retry;}
fail:// 输出警告信息warn_alloc(gfp_mask, ac->nodemask,"page allocation failure: order:%u", order);
got_pg:return page;
}

trace_mm_page_alloc

trace用于跟踪打印信息,俗称“插桩”,通过定义类型、变量和结构,输出相关信息。

TRACE_EVENT(mm_page_alloc,TP_PROTO(struct page *page, unsigned int order,gfp_t gfp_flags, int migratetype), TP_ARGS(page, order, gfp_flags, migratetype), TP_STRUCT__entry(__field(        unsigned long,  pfn             )__field(        unsigned int,   order           )__field(        unsigned long,  gfp_flags       )__field(        int,            migratetype     )),TP_fast_assign(__entry->pfn            = page ? page_to_pfn(page) : -1UL;__entry->order          = order;__entry->gfp_flags      = (__force unsigned long)gfp_flags;__entry->migratetype    = migratetype;),TP_printk("page=%p pfn=0x%lx order=%d migratetype=%d gfp_flags=%s",__entry->pfn != -1UL ? pfn_to_page(__entry->pfn) : NULL,__entry->pfn != -1UL ? __entry->pfn : 0,__entry->order,__entry->migratetype,show_gfp_flags(__entry->gfp_flags))
);

目录预览

<<baddy:初始化内存域>>

baddy:核心函数入口相关推荐

  1. d3d透视逆向篇:第6课 通过特征码定位D3D函数入口地址

    本逆向安全章节是我业余之间编写的,也借鉴了前辈的经验来给大家做一个简单的文字逆向安全教程,欢迎大家讨论和指正,共同学习.禁止非法用途.教程我从最简单的开始给大家做一个简单的讲解d3d 类的3d游戏和f ...

  2. jQuery(动画,核心函数)

    基本的HTML布局 <div id="box"><img src="1.jpg" alt=""><img sr ...

  3. 【FFmpeg】ffmpeg工具源码分析(二):转码核心函数 transcode

    1.转码流程 1)转码前初始化:打开输入输出文件,初始化编码器.解码器.过滤器,创建多线程,设置串口终端等: 2)while循环处理每一包数据,核心函数 transcode_step(稍后分析): 3 ...

  4. VS如何将核心函数封装成dll、lib,并供给第三方调用?

    本文首发于微信公众号[3D视觉工坊],作者原创. 文章目录 前言 一 先封装核心函数,实现功能 二 将核心函数生成dll.lib(此处以debug模式下为例,release模式下相类似) 三 调用dl ...

  5. 【Linux 内核】进程优先级与调度策略 ③ ( 设置、获取线程优先级的核心函数 | 修改线程调度策略函数 )

    文章目录 一.设置.获取线程优先级的核心函数 二.修改线程调度策略函数 一.设置.获取线程优先级的核心函数 设置.获取 线程 优先级的 核心 函数 : ① 设置 " 创建线程 " ...

  6. 【Android 逆向】函数拦截实例 ( ② 插桩操作 | 保存实际函数入口 6 字节数据 | 在插桩的函数入口写入跳转指令 | 构造拼接桩函数 )

    文章目录 前言 一.函数拦截需要的几个参数 二.插桩前先保存实际函数入口 6 字节数据 三.在插桩的函数入口写入跳转指令 | 构造拼接桩函数 前言 [Android 逆向]函数拦截实例 ( 函数拦截流 ...

  7. 【Flutter】Dart 技巧 ( 独立主函数入口 | 可空类型判定 | 默认值设定 )

    文章目录 一.独立主函数入口 二.可空类型判定 三.默认值设定 四.完整代码示例 五. 相关资源 一.独立主函数入口 在 dart 文件中声明 main() 函数 , 即可脱离 Flutter 环境 ...

  8. jQuery的核心函数

    jQuery的核心函数有两个$和jQuery 我们拿两个例子来说一下 这两种代码都可以执行通过

  9. PHP基于数组的分页函数(核心函数array_slice())

    <?php /** * 数组分页函数  核心函数  array_slice * 用此函数之前要先将数据库里面的所有数据按一定的顺序查询出来存入数组中 * $count   每页多少条数据 * $ ...

  10. Mr.J-- jQuery学习笔记(二)--核心函数jQuery对象

    核心函数 函数列表 函数说明:jQuery()    主要用于获取HTML DOM元素并将其封装为jQuery对象 jQuery(): jQuery()函数是jQuery库的最核心函数,jQuery的 ...

最新文章

  1. 传统网站与营销型网站区别盘比
  2. 【Groovy】MOP 元对象协议与元编程 ( 方法合成 | 动态注入方法 )
  3. Java中string字符串的值_Java中的字符串(String)
  4. android SQLite数据库的使用
  5. 新的学期、新的开始、新的付出、新的收获!
  6. 01 - java 开始
  7. 在线预览文档简单例子
  8. BeagleBone Black 连接USB摄像头,并实现采集、存储及传输
  9. Graphviz样例之有限状态自动机
  10. 软考中级网络工程师知识目录
  11. c语言定时器中断实验报告,单片机实验报告-定时器中断实验.doc
  12. Qtdesigner 插入图片(一步到位)
  13. 【C语言】 C 语言 关键字分析 ( 属性关键字 | 常量关键字 | 结构体关键字 | 联合体关键字 | 枚举关键字 | 命名关键字 | 杂项关键字)
  14. 电容降压 20170619 周一
  15. Photoshop 系列:照片调色记录
  16. asciidoc_如何使用AsciiDoc创建博客
  17. 易开发易投产的51单片机时钟计时器:番茄时钟——TFT彩屏显示方案
  18. h5网页检测手机是否安装了app。
  19. H265 数据结构与码流分析
  20. 功能安全产品开发初始分析 | 功能分析

热门文章

  1. java对象为什么要实现序列化
  2. Scrapy框架之Spiders类理解
  3. RationalDMIS7.1定制报告(Excel模板制作)
  4. 解决onenote同步慢的问题
  5. 计算机c盘无法访问,win10系统无法打开C盘,提示拒绝访问的解决办法
  6. text显示下标的字体 unity_Text Mesh Pro中文版
  7. 合并二叉树进行期权定价
  8. 3G模块SIM5360E拨号上网
  9. 电影的幕布效果怎么做?
  10. 什么是生成器 — 一篇文章让你看懂