热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

16页面迁移

linux为页面迁移提供了一个系统调用migrate_pages,最早是在linux2.6.16版本加入的,它可以迁移一个进程的所有页面到指定内存节点上

    linux为页面迁移提供了一个系统调用migrate_pages,最早是在linux2.6.16版本加入的,它可以迁移一个进程的所有页面到指定内存节点上,该系统调用在用户空间的函数接口如下:

#include
long migrate_pages(int pid, unsigned long maxnode, const unsigned long *old_nodes, const unsigned long *new_nodes);

    该系统调用最早是为了在NUMA系统上提供一种能迁移进程到任意内存节点的能力。现在内核除了为NUMA系统提供页迁移能力外,其他的一些模块也可以利用页迁移动能做一些事情,例如内存规整和内存热插拔等。

migrate_page()函数
    页面迁移(page migration)的核心函数是migrate_pages().
[mm/migrate.c]


/** migrate_pages - migrate the pages specified in a list, to the free pages* supplied as the target for the page migration** @from: The list of pages to be migrated.* @get_new_page: The function used to allocate free pages to be used* as the target of the page migration.* @put_new_page: The function used to free target pages if migration* fails, or NULL if no special handling is necessary.* @private: Private data to be passed on to get_new_page()* @mode: The migration mode that specifies the constraints for* page migration, if any.* @reason: The reason for page migration.** The function returns after 10 attempts or if no pages are movable any more* because the list has become empty or no retryable pages exist any more.* The caller should call putback_lru_pages() to return pages to the LRU* or free list only if ret != 0.** Returns the number of pages that were not migrated, or an error code.*/
/*参数说明:
@from: 表示将要迁移的页面链表
@get_new_page: 内存函数指针
@put_new_page: 迁移失败时释放目标页面的函数指针
@private: 传递给get_new_page的参数
@mode: 迁移模式
@reason: 迁移的原因
*/
int migrate_pages(struct list_head *from, new_page_t get_new_page,free_page_t put_new_page, unsigned long private,enum migrate_mode mode, int reason)
{int retry &#61; 1;int nr_failed &#61; 0;int nr_succeeded &#61; 0;int pass &#61; 0;struct page *page;struct page *page2;int swapwrite &#61; current->flags & PF_SWAPWRITE;int rc;if (!swapwrite)current->flags |&#61; PF_SWAPWRITE;/*for循环表示这里会尝试10次&#xff0c;从from链表摘取一个页面&#xff0c;然后调用unmap_add_move()函数进行页面的迁移&#xff0c;返回MIGRATEPAGE_SUCCESS表示迁移成功。*/for(pass &#61; 0; pass <10 && retry; pass&#43;&#43;) {retry &#61; 0;list_for_each_entry_safe(page, page2, from, lru) {cond_resched();if (PageHuge(page))rc &#61; unmap_and_move_huge_page(get_new_page,put_new_page, private, page,pass > 2, mode);else/*下面查看unmap_and_move函数实现*/rc &#61; unmap_and_move(get_new_page, put_new_page,private, page, pass > 2, mode);switch(rc) {case -ENOMEM:goto out;case -EAGAIN:retry&#43;&#43;;break;case MIGRATEPAGE_SUCCESS:nr_succeeded&#43;&#43;;break;default:/** Permanent failure (-EBUSY, -ENOSYS, etc.):* unlike -EAGAIN case, the failed page is* removed from migration page list and not* retried in the next outer loop.*/nr_failed&#43;&#43;;break;}}}rc &#61; nr_failed &#43; retry;
out:if (nr_succeeded)count_vm_events(PGMIGRATE_SUCCESS, nr_succeeded);if (nr_failed)count_vm_events(PGMIGRATE_FAIL, nr_failed);trace_mm_migrate_pages(nr_succeeded, nr_failed, mode, reason);if (!swapwrite)current->flags &&#61; ~PF_SWAPWRITE;return rc;
}

unmap_and_move()函数实现&#xff1a;此函数进行页面迁移。

1.分配新的page

2.尝试迁移旧页面到新页面

[migrate_pages()->unmap_and_move()]

/** Obtain the lock on page, remove all ptes and migrate the page* to the newly allocated page in newpage.*/
static int unmap_and_move(new_page_t get_new_page, free_page_t put_new_page,unsigned long private, struct page *page, int force,enum migrate_mode mode)
{int rc &#61; 0;int *result &#61; NULL;/*调用get_new_page()分配一个新的页面newpage*/struct page *newpage &#61; get_new_page(page, private, &result);if (!newpage)return -ENOMEM;if (page_count(page) &#61;&#61; 1) {/* page was freed from under us. So we are done. */goto out;}if (unlikely(PageTransHuge(page)))if (unlikely(split_huge_page(page)))goto out;/*调用__unmap_and_move()去尝试迁移页面page到新分配的页面newpage中。下面查看此函数的实现*/rc &#61; __unmap_and_move(page, newpage, force, mode);out:/*迁移失败&#xff0c;会把这个页面重新返回LRU链表中。会把新分配的页面释放*/if (rc !&#61; -EAGAIN) {/** A page that has been migrated has all references* removed and will be freed. A page that has not been* migrated will have kepts its references and be* restored.*/list_del(&page->lru);dec_zone_page_state(page, NR_ISOLATED_ANON &#43;page_is_file_cache(page));putback_lru_page(page);}/** If migration was not successful and there&#39;s a freeing callback, use* it. Otherwise, putback_lru_page() will drop the reference grabbed* during isolation.*/if (rc !&#61; MIGRATEPAGE_SUCCESS && put_new_page) {ClearPageSwapBacked(newpage);put_new_page(newpage, private);} else if (unlikely(__is_movable_balloon_page(newpage))) {/* drop our reference, page already in the balloon */put_page(newpage);} else /*迁移成功的话&#xff0c;新分配的页面也会加入到LRU链表中*/putback_lru_page(newpage);if (result) {if (rc)*result &#61; rc;else*result &#61; page_to_nid(newpage);}return rc;
}

__unmap_and_move()函数实现:

[migrate_pages()->unmap_and_move()->__unmap_and_move()]

注意&#xff1a;在migrate_pages()中&#xff0c;当尝试次数大于2时&#xff0c;会设置force等于1

static int __unmap_and_move(struct page *page, struct page *newpage,int force, enum migrate_mode mode)
{int rc &#61; -EAGAIN;int page_was_mapped &#61; 0;struct anon_vma *anon_vma &#61; NULL;/*trylock_page()尝试给page加锁&#xff0c;trylock_page()返回false&#xff0c;表示已经有别的进程给page加锁&#xff0c;返回true表示当前进程可以成功获取锁*/if (!trylock_page(page)) {/*如果尝试获取页面锁不成功&#xff0c;当前不是强制迁移(force等于0)或迁移模式等于异步(mode 等于 MIGRATE_ASYNC),会直接忽略这个page&#xff0c;因为这种情况下没有必要睡眠等待页面释放锁*/if (!force || mode &#61;&#61; MIGRATE_ASYNC)goto out;/** It&#39;s not safe for direct compaction to call lock_page.* For example, during page readahead pages are added locked* to the LRU. Later, when the IO completes the pages are* marked uptodate and unlocked. However, the queueing* could be merging multiple pages for one bio (e.g.* mpage_readpages). If an allocation happens for the* second or third page, the process can end up locking* the same page twice and deadlocking. Rather than* trying to be clever about what pages can be locked,* avoid the use of lock_page for direct compaction* altogether.*//*如果当前进程设置了PF_MEMALLOC标志位&#xff0c;表示可能是在直接内存压缩(direct compaction)的内核路径上&#xff0c;睡眠等待页面锁是不安全的&#xff0c;所以直接忽略page。举个例子&#xff0c;在文件预读中&#xff0c;预读的所有页面都会加页面锁(PG_lock)并添加到LRU链表中&#xff0c;等到预读完成后&#xff0c;这些页面会标记PG_uptodate并释放页面锁&#xff0c;这个过程中块设备会把多个页面合并到一个BIO中(mpage_readpages())。如果在分配第二或第三个页面时发生内存短缺&#xff0c;内核会运行到直接内存压缩(direct compaction)内核路径上&#xff0c;导致一个页面已经加锁了又等待这个锁&#xff0c;产生死锁&#xff0c;因此在直接内存压缩(directcompaction)的内核路径会标记PF_MEMALLOC.PF_MEMALLOC标志位一般是在直接内存压缩&#xff0c;直接内存回收和kswapd设置&#xff0c;这些场景下也可能会有少量的内存分配行为&#xff0c;因此设置PF_MEMALLOC标志位&#xff0c;表示允许它们使用系统预留的内存&#xff0c;即不用考虑Water Mark水位。可以参见__perform_reclaim()、__alloc_pages_direct_compact()和kswapd()等函数*/if (current->flags & PF_MEMALLOC)goto out;lock_page(page);}/*处理正在回写的页面即PG_writeback标志位的页面。这里只有当页面迁移的模式为MIGRATE_SYNC且设置强制迁移(force &#61; 1)时才会去等待这个页面回写完成&#xff0c;否则直接忽略该页面。wait_on_page_writeback()函数会等待页面回写完成。*/if (PageWriteback(page)) {/** Only in the case of a full synchronous migration is it* necessary to wait for PageWriteback. In the async case,* the retry loop is too short and in the sync-light case,* the overhead of stalling is too much*/if (mode !&#61; MIGRATE_SYNC) {rc &#61; -EBUSY;goto out_unlock;}if (!force)goto out_unlock;wait_on_page_writeback(page);}/** By try_to_unmap(), page->mapcount goes down to 0 here. In this case,* we cannot notice that anon_vma is freed while we migrates a page.* This get_anon_vma() delays freeing anon_vma pointer until the end* of migration. File cache pages are no problem because of page_lock()* File Caches may use write_page() or lock_page() in migration, then,* just care Anon page here.*//*处理匿名页面的anon_vma可能被释放的特殊情况&#xff0c;因为接下来try_to_unmap()函数执行完成时&#xff0c;page->mapcount引用计数会变成0.在页面迁移过程中&#xff0c;我们无法知道anon_vma数据结构是否被释放了。page_get_anon_vma()会增加anon_vma->refcount引用计数防止它被其他进程释放&#xff0c;与之对应的是put_anon_vma()减少anon_vma->refcount引用计数&#xff0c;它们是成对出现的。*/if (PageAnon(page) && !PageKsm(page)) {/** Only page_lock_anon_vma_read() understands the subtleties of* getting a hold on an anon_vma from outside one of its mms.*/anon_vma &#61; page_get_anon_vma(page);if (anon_vma) {/** Anon page*/} else if (PageSwapCache(page)) {/** We cannot be sure that the anon_vma of an unmapped* swapcache page is safe to use because we don&#39;t* know in advance if the VMA that this page belonged* to still exists. If the VMA and others sharing the* data have been freed, then the anon_vma could* already be invalid.** To avoid this possibility, swapcache pages get* migrated but are not remapped when migration* completes*/} else {goto out_unlock;}}if (unlikely(isolated_balloon_page(page))) {/** A ballooned page does not need any special attention from* physical to virtual reverse mapping procedures.* Skip any attempt to unmap PTEs or to remap swap cache,* in order to avoid burning cycles at rmap level, and perform* the page migration right away (proteced by page lock).*/rc &#61; balloon_page_migrate(newpage, page, mode);goto out_unlock;}/** Corner case handling:* 1. When a new swap-cache page is read into, it is added to the LRU* and treated as swapcache but it has no rmap yet.* Calling try_to_unmap() against a page->mapping&#61;&#61;NULL page will* trigger a BUG. So handle it here.* 2. An orphaned page (see truncate_complete_page) might have* fs-private metadata. The page can be picked up due to memory* offlining. Everywhere else except page reclaim, the page is* invisible to the vm, so the page can not be migrated. So try to* free the metadata, so the page can be freed.*//*这里处理一种特殊情况&#xff0c;例如一个swap cache页面发生swap-in时&#xff0c;在do_swap_page()中会分配一个新的页面&#xff0c;该页面添加到LRU链表中&#xff0c;这个页面是swapcache页面&#xff0c;但是它 还没有建立RAMP关系&#xff0c;因此page->mapping等于NULL&#xff0c;接下来要进行的try_to_unmap()函数处理这种页面会触发bug。*/if (!page->mapping) {VM_BUG_ON_PAGE(PageAnon(page), page);if (page_has_private(page)) {try_to_free_buffers(page);goto out_unlock;}goto skip_unmap;}/* Establish migration ptes or remove ptes *//*对于有pte映射的页面&#xff0c;调用try_to_unmap()解除页面所有映射的pte。try_to_unmap()定义在mm/rmap.c*/if (page_mapped(page)) {try_to_unmap(page,TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);page_was_mapped &#61; 1;}skip_unmap:/*对于已经解除完所有映射的页面&#xff0c;调用move_to_new_page()迁移到新的页面new_page&#xff0c; 下面查看此函数实现*/if (!page_mapped(page))rc &#61; move_to_new_page(newpage, page, page_was_mapped, mode);/*对于迁移页面失败&#xff0c;调用remove_migration_ptes()把迁移失败页面的pte设置回原来的位置*/if (rc && page_was_mapped)remove_migration_ptes(page, page);/* Drop an anon_vma reference if we took one */if (anon_vma)put_anon_vma(anon_vma);out_unlock:unlock_page(page);
out:return rc;
}

move_to_new_page()函数实现&#xff1a;

[migrate_pages()->unmap_and_move()->__unmap_and_move()->move_to_new_page()]

/** Move a page to a newly allocated page* The page is locked and all ptes have been successfully removed.** The new page will have replaced the old page if this function* is successful.** Return value:* <0 - error code* MIGRATEPAGE_SUCCESS - success*/
static int move_to_new_page(struct page *newpage, struct page *page,int page_was_mapped, enum migrate_mode mode)
{struct address_space *mapping;int rc;/** Block others from accessing the page when we get around to* establishing additional references. We are the only one* holding a reference to the new page at this point.*//*如果newpage已经被其他进程加锁&#xff0c;那么会是一个bug&#xff0c;调用BUG函数来处理*/if (!trylock_page(newpage))BUG();/* Prepare mapping for the new page.*//*设置newpage的index和mapping和PG_SwapBacked标志位*/newpage->index &#61; page->index;newpage->mapping &#61; page->mapping;if (PageSwapBacked(page))SetPageSwapBacked(newpage);/*处理页面mapping情况&#xff0c;page_mapping()函数获取page->mapping指针&#xff0c;查看下面对其解释*/mapping &#61; page_mapping(page);/*下面以匿名页面为例&#xff0c;调用migrate_page()将旧页面的相关信息迁移到新页面。对于其他有mapping的页面&#xff0c;会调用mapping指向的migratepage()函数指针或fallback_migrate_page()函数&#xff0c;很多文件系统都提供这样的函数接口。下面查看migrate_page()函数*/if (!mapping)rc &#61; migrate_page(mapping, newpage, page, mode);else if (mapping->a_ops->migratepage)/** Most pages have a mapping and most filesystems provide a* migratepage callback. Anonymous pages are part of swap* space which also has its own migratepage callback. This* is the most common path for page migration.*/rc &#61; mapping->a_ops->migratepage(mapping,newpage, page, mode);elserc &#61; fallback_migrate_page(mapping, newpage, page, mode);if (rc !&#61; MIGRATEPAGE_SUCCESS) {newpage->mapping &#61; NULL;} else {mem_cgroup_migrate(page, newpage, false);if (page_was_mapped)remove_migration_ptes(page, newpage); /*迁移页面的每一个pte&#xff0c;下面查看此函数的实现*/page->mapping &#61; NULL;}unlock_page(newpage);return rc;
}

page_mapping()函数:获取page->mapping指针

[mm/util.c]

struct address_space *page_mapping(struct page *page)
{struct address_space *mapping &#61; page->mapping;/* This happens if someone calls flush_dcache_page on slab page *//*如果page属于slab或是匿名页面&#xff0c;该函数返回mapping为空*/if (unlikely(PageSlab(page)))return NULL;/*如果是PageSwapCache()&#xff0c;则返回swap_address_space空间&#xff0c;其余为page cache的情况&#xff0c;直接返回page->mapping*/if (unlikely(PageSwapCache(page))) {swp_entry_t entry;entry.val &#61; page_private(page);mapping &#61; swap_address_space(entry);} else if ((unsigned long)mapping & PAGE_MAPPING_ANON)mapping &#61; NULL;return mapping;
}
回到move_to_new_page()函数

migrate_page()函数实现&#xff1a;

[migrate_pages()->unmap_and_move()->__unmap_and_move()->move_to_new_page()->migrate_page()]

/** Common logic to directly migrate a single page suitable for* pages that do not use PagePrivate/PagePrivate2.** Pages are locked upon entry and exit.*/
int migrate_page(struct address_space *mapping,struct page *newpage, struct page *page,enum migrate_mode mode)
{int rc;BUG_ON(PageWriteback(page)); /* Writeback must be complete *//*对于匿名页面来说&#xff0c;此函数没有做任何事情*/rc &#61; migrate_page_move_mapping(mapping, newpage, page, NULL, mode, 0);if (rc !&#61; MIGRATEPAGE_SUCCESS)return rc;/*将旧页面的内容复制到新页面中&#xff0c;下面查看此函数的实现*/migrate_page_copy(newpage, page);return MIGRATEPAGE_SUCCESS;
}回到move_to_new_page

migrate_page_copy()函数实现

[migrate_pages()->unmap_and_move()->__unmap_and_move()->move_to_new_page()->migrate_page()->migrate_page_copy()]

/** Copy the page to its new location*/
void migrate_page_copy(struct page *newpage, struct page *page)
{int cpupid;if (PageHuge(page) || PageTransHuge(page))copy_huge_page(newpage, page);elsecopy_highpage(newpage, page); /*复制旧页面的内容到新页面中&#xff0c;使用kmap_atomic()函数来映射页面以便读取页面的内容。*//*下面依照旧页面中flags的比特位来设置newpage相应的标志位&#xff0c;例如PG_error、PG_referenced、PG_uptodate、PG_active、PG_unevictable、PG_checked和PG_mappedtodisk等*/if (PageError(page))SetPageError(newpage);if (PageReferenced(page))SetPageReferenced(newpage);if (PageUptodate(page))SetPageUptodate(newpage);if (TestClearPageActive(page)) {VM_BUG_ON_PAGE(PageUnevictable(page), page);SetPageActive(newpage);} else if (TestClearPageUnevictable(page))SetPageUnevictable(newpage);if (PageChecked(page))SetPageChecked(newpage);if (PageMappedToDisk(page))SetPageMappedToDisk(newpage);/*处理旧页面是dirty的情况。如果旧页面是匿名页面(PageSwapBacked(page)),则设置PG_dirty位&#xff1b;如果旧页面是page cache&#xff0c;则由__set_page_dirty_nobuffers()设置radix_tree中dirty标志位。*/if (PageDirty(page)) {clear_page_dirty_for_io(page);/** Want to mark the page and the radix tree as dirty, and* redo the accounting that clear_page_dirty_for_io undid,* but we can&#39;t use set_page_dirty because that function* is actually a signal that all of the page has become dirty.* Whereas only part of our page may be dirty.*/if (PageSwapBacked(page))SetPageDirty(newpage);else__set_page_dirty_nobuffers(newpage);}/** Copy NUMA information to the new page, to prevent over-eager* future migrations of this same page.*/cpupid &#61; page_cpupid_xchg_last(page, -1);page_cpupid_xchg_last(newpage, cpupid);mlock_migrate_page(newpage, page);/*处理旧页面是KSM页面的情况*/ksm_migrate_page(newpage, page);/** Please do not reorder this without considering how mm/ksm.c&#39;s* get_ksm_page() depends upon ksm_migrate_page() and PageSwapCache().*/ClearPageSwapCache(page);ClearPagePrivate(page);set_page_private(page, 0);/** If any waiters have accumulated on the new page then* wake them up.*/if (PageWriteback(newpage))end_page_writeback(newpage);
}回到migrate_page()函数

remove_migration_ptes()函数实现

[migrate_pages()->unmap_and_move()->__unmap_and_move()->move_to_new_page()->remove_migration_ptes()]

/** Get rid of all migration entries and replace them by* references to the indicated page.*/
static void remove_migration_ptes(struct page *old, struct page *new)
{struct rmap_walk_control rwc &#61; {.rmap_one &#61; remove_migration_pte,.arg &#61; old,};rmap_walk(new, &rwc);
}

remove_migration_ptes()是典型地利用RMAP反向映射系统找到映射旧页面的每个PTE&#xff0c;直接来看它的rmap_one函数指针。

/** Restore a potential migration pte to a working pte entry*/
/*remove_migration_pte函数找到其中一个映射的虚拟地址&#xff0c;根据参数中的vma和addr。*/
static int remove_migration_pte(struct page *new, struct vm_area_struct *vma,unsigned long addr, void *old)
{struct mm_struct *mm &#61; vma->vm_mm;swp_entry_t entry;pmd_t *pmd;pte_t *ptep, pte;spinlock_t *ptl;if (unlikely(PageHuge(new))) {ptep &#61; huge_pte_offset(mm, addr);if (!ptep)goto out;ptl &#61; huge_pte_lockptr(hstate_vma(vma), mm, ptep);} else {/*通过mm和虚拟地址addr找到相应的页表项pte。*/pmd &#61; mm_find_pmd(mm, addr);if (!pmd)goto out;ptep &#61; pte_offset_map(pmd, addr);/** Peek to check is_swap_pte() before taking ptlock? No, we* can race mremap&#39;s move_ptes(), which skips anon_vma lock.*/ptl &#61; pte_lockptr(mm, pmd);}/*每个进程的mm数据结构中有一个保护页表的spinlock锁(mm->page_table_lock)*/spin_lock(ptl);/*把映射的pte页表项的内容设置到新页面的pte中&#xff0c;相当于重新建立映射关系。*/pte &#61; *ptep;if (!is_swap_pte(pte))goto unlock;entry &#61; pte_to_swp_entry(pte);if (!is_migration_entry(entry) ||migration_entry_to_page(entry) !&#61; old)goto unlock;get_page(new);pte &#61; pte_mkold(mk_pte(new, vma->vm_page_prot));if (pte_swp_soft_dirty(*ptep))pte &#61; pte_mksoft_dirty(pte);/* Recheck VMA as permissions can change since migration started */if (is_write_migration_entry(entry))pte &#61; maybe_mkwrite(pte, vma);#ifdef CONFIG_HUGETLB_PAGEif (PageHuge(new)) {pte &#61; pte_mkhuge(pte);pte &#61; arch_make_huge_pte(pte, vma, new, 0);}
#endifflush_dcache_page(new);set_pte_at(mm, addr, ptep, pte);if (PageHuge(new)) {if (PageAnon(new))hugepage_add_anon_rmap(new, vma, addr);elsepage_dup_rmap(new);/*将新的页面newpage添加到RMAP反向映射系统中。*/} else if (PageAnon(new))page_add_anon_rmap(new, vma, addr);elsepage_add_file_rmap(new);/* No need to invalidate - it was non-present before *//*调用update_mmu_cache()更新相应的cache。增加一个新的PTE&#xff0c;或者修改PTE时需要调用该函数对cache进行管理&#xff0c;对于ARMv6以上的CPU来说&#xff0c;该函数是空函数&#xff0c;cache一致性管理在set_pte_at()函数中完成。*/update_mmu_cache(vma, addr, ptep);
unlock:pte_unmap_unlock(ptep, ptl);
out:return SWAP_AGAIN;
}

内核中有多处使用到页的迁移的功能&#xff0c;列出如下。

  • 内存规整(memory compaction)

  • 内存热插拔(memory hotplug)

  • NUMA系统&#xff0c;系统有一个sys_migrate_pages的系统调用。


推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • PHP中的单例模式与静态变量的区别及使用方法
    本文介绍了PHP中的单例模式与静态变量的区别及使用方法。在PHP中,静态变量的存活周期仅仅是每次PHP的会话周期,与Java、C++不同。静态变量在PHP中的作用域仅限于当前文件内,在函数或类中可以传递变量。本文还通过示例代码解释了静态变量在函数和类中的使用方法,并说明了静态变量的生命周期与结构体的生命周期相关联。同时,本文还介绍了静态变量在类中的使用方法,并通过示例代码展示了如何在类中使用静态变量。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了在CentOS 6.4系统中更新源地址的方法,包括备份现有源文件、下载163源、修改文件名、更新列表和系统,并提供了相应的命令。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
author-avatar
Fmyu的守护天使
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有