十天学Linux内核之第七天---电源开和关时都发生了什么

十天学Linux内核之第七天---电源开和关时都发生了什么
原文:十天学Linux内核之第七天---电源开和关时都发生了什么

  说实话感觉自己快写不下去了,其一是有些勉强跟不上来,其二是感觉自己越写越差,刚开始可能是新鲜感以及很多读者的鼓励,现在就是想快点完成自己制定的任务,不过总有几个读者给自己鼓励,很欣慰的事情,不多感慨了,加紧时间多多去探索吧,今天要去描述的是电源开和关时都发生了什么,一起去看看吧~~

  bootloader引导装入程序将内核映像加载到内存并处理控制权传送到内核后在内核引导时每个子系统都必须要初始化,我们根据实际执行的线性顺序跟踪内核的初始化过程,下图说明了从系统加电到断电这一过程中所有事情发生的顺序,这个图不多加解释了,看图就知道其线性执行顺序,中间过程也是很简单的步骤啦。

  我们首先讨论的是BIOS和Open和Fireware,它们分别是x86和PPC系统加电后在只读内存的某一地址(一般是Flash ROM)最先运行的代码,这些代码负责激活系统中相应的部分,以便处理内核的加载,对于x86而言,这就是系统BIOS的驻留之处,基本的输入输出是一块引导系统并与硬件相关的系统初始化代码,这里不多说了,对于PowerPC而言,初始化代码的类型与PowerPC体系结构的出现时间有关,详情就参见Open Fireware 的主页www.openfireware.org

  引导装入程序Bootloaders大家应该早就有所了解了,Boot Loaders是驻留在计算机引导设备的程序,第一个引导设备往往是系统中的第一个硬盘,完成足够的系统初始化工作后,BIOS或固件调用引导装入程序,一旦成功加载进来,内核就初始化并配置操作系统,对x86系统而言,BIOS允许用户为其系统设置引导设备的顺序,这里提一下GRUB,Grand Unified Bootloader 是基于x86的引导装入程序,用来加载Linux。GRUB2在设计之初就考虑了移植到PPC系统的问题,哎具体的就www.gnu.org/software/grub上有丰富的文档,并且百度上也有相关的例程之类的,我觉得这里我就不再阐释了。

  Linux装入程序即LILO这个我还是得说说,和GRUB相类似但是LILO仅仅使用配置文件,并且没有命令行接口。LILO运行时的第一阶段步骤如下:

第一阶段:

  • 开始执行并且显示“L.";
  • 检测磁盘几何信息并且显示“I.";
  • 加载第二阶段的代码。

第二阶段:

  • 开始执行并且显示“L.";
  • 确定引导数据和操作系统的位置,并且显示”O.";

确定启动哪个操作系统并且跳转到该操作系统,LILO配置文件中的一段代码如下(代码在etc/lilo.conf上可以查看):

1 image = /boot/bzimage-2.6.7-mytestkernel  //image指明内核所在地
2 label = Kernel 2.6.7, my test kernel  //label描述配置的字符串
3 root = /dev/hda6          //root指明根文件系统驻留的分区
4 read-only              //表明根分区在引导时候不可被修改

  GRUB和LILO的主要区别

  • LILO将配置信息存储在主引导记录中,若有任何改动,必须运行/sbin/lilo来更新主引导记录
  • LILO没有交互式的命令行接口
  • LILO不能读取不同的文件系统

  最后说一下Yaboot,yaboot是另一个引导程序(比如说,grub和lilo是比较出名的引导程序),用于Macintosh。主页:http://yaboot.ozlabs.org/ Yaboot引导时的步骤如下:

  • OF调用Yaboot
  • 找到引导设备和引导路径并打开引导分区
  • 打开/etc/yaboot.conf或命令解释器
  • 加载映像或内核以及initrd
  • 执行映像

这里需要在Ubuntu上自己操作,当然对Ubuntu的基本命令操作是首先要了解的,然后才能根据步骤一步一步执行下去。

 

  x86和PowerPC体系结构的硬件初始化,由于内存管理的初始化与硬件息息相关,要理解其初始化过程就必须了解硬件的规格,那么都只能去看看资料才能了解了,这里我多讲不了。现在的PowerPC和x86的代码都集中在init/main.c的start_kernel()中,该例程位于体系结构无关的代码段,它调用特定体系结构的例程来完成内存初始化。下面我们来探究一下start_kernel()函数。

  跳转到start_kernel()时候,执行进程0,也就是平时说的超级用户进程,进程0孕育了进程1,也就是init进程,然后进程0就变成CPU的空闲进程,调/sbin/init时,仅有这两个进程在运行:

  1 asmlinkage void __init start_kernel(void)  
  2 {  
  3 char * command_line;  
  4 extern struct kernel_param __start___param[], __stop___param[];  
  5 //来设置smp process id,当然目前看到的代码里面这里是空的  
  6 smp_setup_processor_id();  
  7 //lockdep是linux内核的一个调试模块,用来检查内核互斥机制尤其是自旋锁潜在的死锁问题。  
  8 //自旋锁由于是查询方式等待,不释放处理器,比一般的互斥机制更容易死锁,  
  9 //故引入lockdep检查以下几种情况可能的死锁(lockdep将有专门的文章详细介绍,在此只是简单列举):  
 10 //  
 11 //·同一个进程递归地加锁同一把锁;  
 12 //  
 13 //·一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作,  
 14 // 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;  
 15 //  
 16 //·加锁后导致依赖图产生成闭环,这是典型的死锁现象。  
 17 lockdep_init();  
 18 debug_objects_early_init();  
 19 //初始化stack_canary栈3  
 20 //stack_canary的是带防止栈溢出攻击保护的堆栈。  
 21 //  当user space的程序通过int 0x80进入内核空间的时候,CPU自动完成一次堆栈切换,   
 22 //从user space的stack切换到kernel space的stack。  
 23 //   在这个进程exit之前所发生的所有系统调用所使用的kernel stack都是同一个。  
 24 //kernel stack的大小一般为4096/8192,  
 25 //内核堆栈示意图帮助大家理解:  
 26 //  
 27 //   内存低址                                                              内存高址  
 28 //                      |                 |<-----------------------------esp|     
 29 //   +-----------------------------------4096-------------------------------+  
 30 //   |        72        |     4           |       x < 4016    |     4       |  
 31 //   +------------------+-----------------+---------------------------------+  
 32 //   |thread_info  |    | STACK_END_MAGIC |   var/call chain  |stack_canary |  
 33 //   +------------------+-----------------+---------------------------------+  
 34 //   |     28      | 44 |                 |                                 |  
 35 //                 V    |                                                   |  
 36 //             restart_block                                                V  
 37 //  
 38 //esp+0x0                                                                   +0x40  
 39 //    +---------------------------------------------------------------------------+  
 40 //    |ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss|  
 41 //    +---------------------------------------------------------------------------+  
 42 //    |             kernel完成                          |         cpu自动完成       |  
 43 boot_init_stack_canary();   
 44 //  cgroup: 它的全称为control group.即一组进程的行为控制.  
 45 //  比如,我们限制进程/bin/sh的CPU使用为20%.我们就可以建一个cpu占用为20%的cgroup.  
 46 //  然后将/bin/sh进程添加到这个cgroup中.当然,一个cgroup可以有多个进程.  
 47 
 48 cgroup_init_early();  
 49 //更新kernel中的所有的立即数值,但是包括哪些需要再看?  
 50 core_imv_update();  
 51 //关闭当前CUP中断  
 52 local_irq_disable();  
 53 //修改标记early_boot_irqs_enabled;  
 54 //通过一个静态全局变量 early_boot_irqs_enabled来帮助我们调试代码,  
 55 //通过这个标记可以帮助我们知道是否在”early bootup code”,也可以通过这个标志警告是有无效的终端打开  
 56 early_boot_irqs_off();  
 57 //每一个中断都有一个IRQ描述符(struct irq_desc)来进行描述。  
 58 //这个函数的主要作用是设置所有的 IRQ描述符(struct irq_desc)的锁是统一的锁,  
 59 //还是每一个IRQ描述符(struct irq_desc)都有一个小锁。  
 60 early_init_irq_lock_class();  
 61 
 62 // 大内核锁(BKL--Big Kernel Lock)  
 63 //大内核锁本质上也是自旋锁,但是它又不同于自旋锁,自旋锁是不可以递归获得锁的,因为那样会导致死锁。  
 64 //但大内核锁可以递归获得锁。大内核锁用于保护整个内核,而自旋锁用于保护非常特定的某一共享资源。  
 65 //进程保持大内核锁时可以发生调度,具体实现是:  
 66 //在执行schedule时,schedule将检查进程是否拥有大内核锁,如果有,它将被释放,以致于其它的进程能够获得该锁,  
 67 //而当轮到该进程运行时,再让它重新获得大内核锁。注意在保持自旋锁期间是不运行发生调度的。  
 68 //需要特别指出,整个内核只有一个大内核锁,其实不难理解,内核只有一个,而大内核锁是保护整个内核的,当然有且只有一个就足够了。  
 69 //还需要特别指出的是,大内核锁是历史遗留,内核中用的非常少,一般保持该锁的时间较长,因此不提倡使用它。  
 70 //从2.6.11内核起,大内核锁可以通过配置内核使其变得可抢占(自旋锁是不可抢占的),这时它实质上是一个互斥锁,使用信号量实现。  
 71 //大内核锁的API包括:  
 72 //  
 73 //void lock_kernel(void);  
 74 //  
 75 //该函数用于得到大内核锁。它可以递归调用而不会导致死锁。  
 76 //  
 77 //void unlock_kernel(void);  
 78 //  
 79 //该函数用于释放大内核锁。当然必须与lock_kernel配对使用,调用了多少次lock_kernel,就需要调用多少次unlock_kernel。  
 80 //大内核锁的API使用非常简单,按照以下方式使用就可以了:  
 81 //lock_kernel(); //对被保护的共享资源的访问 … unlock_kernel();  
 82 lock_kernel();  
 83 //初始化time ticket,时钟  
 84 tick_init();  
 85 //函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素:  
 86 // tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,  
 87 //应该执行的操作,该回调函数为 tick_notify   
 88 boot_cpu_init();  
 89 //初始化页地址,当然对于arm这里是个空函数  
 90 page_address_init();  
 91 printk(KERN_NOTICE "%s", linux_banner);  
 92 //系结构相关的内核初始化过程  
 93 setup_arch(&command_line);  
 94 //初始化内存管理  
 95 mm_init_owner(&init_mm, &init_task);  
 96 //处理启动命令,这里就是设置的cmd_line  
 97 setup_command_line(command_line);  
 98 //这个在定义了SMP的时候有作用,现在这里为空函数;对于smp的使用,后面在看。。。  
 99 setup_nr_cpu_ids();  
100 //如果没有定义CONFIG_SMP宏,则这个函数为空函数。  
101 //如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,  
102 //并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。  
103 setup_per_cpu_areas();  
104 //定义在include/asm-x86/smp.h。  
105 //如果是SMP环境,则设置boot CPU的一些数据。在引导过程中使用的CPU称为boot CPU  
106 smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */  
107 //设置node 和 zone 数据结构  
108 //内存管理的讲解:
109 build_all_zonelists(NULL);  
110 //初始化page allocation相关结构  
111 page_alloc_init();  
112 printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);  
113 //解析内核参数  
114 parse_early_param();  
115 parse_args("Booting kernel", static_command_line, __start___param,  
116   __stop___param - __start___param,  
117   &unknown_bootoption);  
118 
119 //初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照实际的物理内存初始化pid hash表  
120 //这里涉及到进程管理
121 pidhash_init();  
122 //初始化VFS的两个重要数据结构dcache和inode的缓存。  
123 vfs_caches_init_early();  
124 //把编译期间,kbuild设置的异常表,也就是__start___ex_table和__stop___ex_table之中的所有元素进行排序  
125 sort_main_extable();  
126 //初始化中断向量表  
127 trap_init();  
128 //memory map初始化  
129 mm_init();  
130 //核心进程调度器初始化,调度器的初始化的优先级要高于任何中断的建立,  
131 //并且初始化进程0,即idle进程,但是并没有设置idle进程的NEED_RESCHED标志,  
132 //所以还会继续完成内核初始化剩下的事情。  
133 //这里仅仅为进程调度程序的执行做准备。  
134 //它所做的具体工作是调用init_bh函数(kernel/softirq.c)把timer,tqueue,immediate三个人物队列加入下半部分的数组  
135 sched_init();  
136 //抢占计数器加1   
137 preempt_disable();  
138 //检查中断是否打开  
139 if (!irqs_disabled()) {  
140 printk(KERN_WARNING "start_kernel(): bug: interrupts were "  
141 "enabled *very* early, fixing it/n");  
142 local_irq_disable();  
143 }  
144 //Read-Copy-Update的初始化  
145 //RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,  
146 //从RCU(read-copy-update)的名称上看,我们就能对他的实现机制有一个大概的了解,  
147 //在修改数据的时候,首先需要读取数据,然后生成一个副本,对副本进行修改,  
148 //修改完成之后再将老数据update成新的数据,此所谓RCU。  
149 rcu_init();  
150 //定义在lib/radix-tree.c。  
151 //Linux使用radix树来管理位于文件系统缓冲区中的磁盘块,  
152 //radix树是trie树的一种  
153 radix_tree_init();  
154 /* init some links before init_ISA_irqs() */  
155 //early_irq_init 则对数组中每个成员结构进行初始化,   
156 //例如, 初始每个中断源的中断号.其他的函数基本为空.   
157 early_irq_init();  
158 //初始化IRQ中断和终端描述符。  
159 //初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],  
160 //把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,  
161 //并初始化该中断的链表表头成员结构变量pend  
162 init_IRQ();  
163 //prio-tree是一棵查找树,管理的是什么?  
164 //http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx  
165 prio_tree_init();  
166 //初始化定时器Timer相关的数据结构  
167 init_timers();  
168 //对高精度时钟进行初始化  
169 hrtimers_init();  
170 //软中断初始化  
171 softirq_init();  
172 //初始化时钟源  
173 timekeeping_init();  
174 //初始化系统时间,  
175 //检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,  
176 //如果为空将其指向dummy_gettimeoffset()函数。  
177 time_init();  
178 //profile只是内核的一个调试性能的工具,  
179 //这个可以通过menuconfig中的Instrumentation Support->profile打开。  
180 profile_init();  
181 if (!irqs_disabled())  
182 printk(KERN_CRIT "start_kernel(): bug: interrupts were "  
183 "enabled early/n");  
184 //与开始的early_boot_irqs_off相对应  
185 early_boot_irqs_on();  
186 //与local_irq_disbale相对应,开中断  
187 local_irq_enable();  
188 gfp_allowed_mask = __GFP_BITS_MASK;  
189 //memory cache的初始化  
190 kmem_cache_init_late();  
191 //初始化控制台以显示printk的内容,在此之前调用的printk,只是把数据存到缓冲区里,  
192 //只有在这个函数调用后,才会在控制台打印出内容  
193 //该函数执行后可调用printk()函数将log_buf中符合打印级别要求的系统信息打印到控制台上。  
194 console_init();  
195 if (panic_later)  
196 panic(panic_later, panic_param);  
197 //如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做  
198 lockdep_info();  
199 
200 //如果定义CONFIG_DEBUG_LOCKING_API_SELFTESTS宏  
201 //则locking_selftest()是一个空函数,否则执行锁自测  
202 locking_selftest();  
203 #ifdef CONFIG_BLK_DEV_INITRD  
204 if (initrd_start && !initrd_below_start_ok &&  
205    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {  
206 printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "  
207    "disabling it./n",  
208    page_to_pfn(virt_to_page((void *)initrd_start)),  
209    min_low_pfn);  
210 initrd_start = 0;  
211 }  
212 #endif  
213 //页面初始化,可以参考上面的cgroup机制  
214 page_cgroup_init();  
215 //页面分配debug启用  
216 enable_debug_pagealloc();  
217 //此处函数为空  
218 kmemtrace_init();  
219 //memory lead侦测初始化,如何侦测???  
220 kmemleak_init();  
221 
222 //在kmem_caches之后表示建立一个高速缓冲池,建立起SLAB_DEBUG_OBJECTS标志。???  
223 debug_objects_mem_init();  
224 //idr在linux内核中指的就是整数ID管理机制,  
225 //从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制  
226 //idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。  
227 idr_init_cache();  
228 //是否是对SMP的支持,单核是否需要??这个要分析  
229 setup_per_cpu_pageset();  
230 //NUMA (Non Uniform Memory Access) policy   
231 //具体是什么不懂  
232 numa_policy_init();  
233 if (late_time_init)  
234 late_time_init();  
235 //初始化调度时钟  
236 sched_clock_init();  
237 //calibrate_delay()函数可以计算出cpu在一秒钟内执行了多少次一个极短的循环,  
238 //计算出来的值经过处理后得到BogoMIPS 值,  
239 //Bogo是Bogus(伪)的意思,MIPS是millions of instructions per second(百万条指令每秒)的缩写。  
240 //这样我们就知道了其实这个函数是linux内核中一个cpu性能测试函数。  
241 calibrate_delay();  
242 //PID是process id的缩写  
243 pidmap_init();  
244 //来自mm/rmap.c  
245 //分配一个anon_vma_cachep作为anon_vma的slab缓存。  
246 //这个技术是PFRA(页框回收算法)技术中的组成部分。  
247 //这个技术为定位而生——快速的定位指向同一页框的所有页表项。  
248 anon_vma_init();  
249 #ifdef CONFIG_X86  
250 if (efi_enabled)  
251 efi_enter_virtual_mode();  
252 #endif  
253 //创建thread_info缓存  
254 thread_info_cache_init();  
255 //申请了一个slab来存放credentials??????如何理解?  
256 cred_init();  
257 //根据物理内存大小计算允许创建进程的数量  
258 fork_init(totalram_pages);  
259 //给进程的各种资源管理结构分配了相应的对象缓存区  
260 proc_caches_init();  
261 //创建 buffer_head SLAB 缓存  
262 buffer_init();  
263 //初始化key的management stuff  
264 key_init();  
265 //关于系统安全的初始化,主要是访问控制    
266 security_init();  
267 //与debug kernel相关  
268 dbg_late_init();  
269 //调用kmem_cache_create()函数来为VFS创建各种SLAB分配器缓存  
270 //包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四个SLAB分配器缓存  
271 vfs_caches_init(totalram_pages);  
272 //创建信号队列  
273 signals_init();  
274 //回写相关的初始化  
275 page_writeback_init();  
276 #ifdef CONFIG_PROC_FS  
277 proc_root_init();  
278 #endif  
279 //它将剩余的subsys初始化.然后将init_css_set添加进哈希数组css_set_table[ ]中.  
280 //在上面的代码中css_set_hash()是css_set_table的哈希函数.  
281 //它是css_set->subsys为哈希键值,到css_set_table[ ]中找到对应项.然后调用hlist_add_head()将init_css_set添加到冲突项中.  
282 //然后,注册了cgroup文件系统.这个文件系统也是我们在用户空间使用cgroup时必须挂载的.  
283 //最后,在proc的根目录下创建了一个名为cgroups的文件.用来从用户空间观察cgroup的状态.  
284 cgroup_init();  
285 cpuset_init();  
286 ////进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存  
287 taskstats_init_early();  
288 delayacct_init();  
289 //此处为一空函数  
290 imv_init_complete();  
291 //测试CPU的各种缺陷,记录检测到的缺陷,以便于内核的其他部分以后可以使用他们工作。  
292 check_bugs();  
293 //电源相关的初始化  
294 acpi_early_init(); /* before LAPIC and SMP init */  
295 //  
296 sfi_init_late();  
297 ftrace_init();  
298 //创建1号进程,详细分析之  
299 rest_init();  
300 }  
下面将一些内核引导的函数归类,以后查询就可以使这个样子的,,,这些都是我百度到的,总结的如下,如果想了解每个函数的含义,可以参考一个文档,我把链接附在下面,http://wenku.baidu.com/link?url=BW4g5AS9KHtvkHorS90lbFOAac2HZFgErLaqeiQr-fejBuRWu8bF28LxKKKxGwGuaQI0ALSUsJeY_dj6m_jgkxX9ozZiE17U0i__sZk_YYa
CPU初始化
smp_setup_processor_id()
boot_cpu_init()
setup_arch(&command_line);
setup_nr_cpu_ids()
setup_per_cpu_areas()
smp_prepare_boot_cpu()
setup_per_cpu_pageset(); 
calibrate_delay();
cpuset_init();
内存管理初始化
boot_init_stack_canary()
page_address_init();
mm_init_owner();
page_alloc_init(); 
mm_init();
rcu_init();
kmem_cache_init_late();
page_cgroup_init(); 
kmemleak_init(); 
numa_policy_init();
anon_vma_init();
page_writeback_init();
进程管理
pidhash_init();
sched_init(); 
sched_clock_init()
pidmap_init();
fork_init(totalram_pages); 
taskstats_init_early();
文件系统
vfs_caches_init_early(); 
thread_info_cache_init();
vfs_caches_init(totalram_pages);
中断
early_irq_init();
init_IRQ(); 
softirq_init();
同步互斥
lockdep_init();
lockdep_info();
locking_selftest(); 
时钟
tick_init(); 
init_timers(); 
hrtimers_init();
timekeeping_init();
time_init();
调试
debug_objects_early_init();
console_init();
enable_debug_pagealloc(); 
debug_objects_mem_init();
dbg_late_init();
其他
sort_main_extable();
trap_init(); 
efi_enter_virtual_mode();
cred_init(); 
proc_caches_init(); 
buffer_init();
key_init(); 
security_init();
signals_init();
proc_root_init();
delayacct_init();
check_bugs();
acpi_early_init();
sfi_init_late(); 
未知
cgroup_init_early();
build_all_zonelists(NULL); 
preempt_disable();
radix_tree_init();
prio_tree_init(); 
profile_init();
idr_init_cache(); 
cgroup_init();
ftrace_init();

  最后来总结一下Linux内核的初始化过程:

  • 启动和锁住内核
  • 为Linux的内存管理初始化页高速缓存和页面地址
  • 为多CPU做好准备
  • 显示内核标志
  • 初始化Linux调度程序
  • 分析传到Linux内核的参数
  • 初始化中断处理程序,定时器处理程序和信号处理程序
  • 挂载初始文件系统
  • 完成系统的初始化,并且将控制权从init交回系统

 

 小结

  今天主要是描述了系统加电和断电时候的内核引导期间发生了什么事情,先讨论了BIOS和Firmware以及它们是如何与内核引导装入程序交互的,也讨论了装入程序LILO,GRUB和Yaboot,,最后着重分析了start_kernel()函数的代码,这个是我看网上代码的,主要是别人分析的太好了,所以借鉴了一下,只要能懂就行,最后列出了一系列的函数,只要看了链接上的 都能懂的,,我也努力在看,,共同进步吧大家~

 

  版权所有,转载请注明转载地址:http://www.cnblogs.com/lihuidashen/p/4250095.html

posted on 2015-01-27 09:11 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/4251897.html

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

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

相关文章

dede php 里加nofollow,织梦导航栏目如何加nofollow

很多用织梦程序做网站的朋友会发现&#xff0c;dedecms后台并没有直接加nofollow的选项。那如果想要给联系我们、关于我们等导航栏目加上nofollow该怎么解决呢&#xff1f;笔者在网上也找了一些资料&#xff0c;有通过修改数据库增加参数的方法&#xff0c;但没有成功。今天笔者…

【Cocos2d-Js实战教学(1)横版摇杆八方向移动】

本教程主要通过搭建一个横版摇杆八方向移动的实例&#xff0c;让大家如何用Cocos2dx-Js来做一款游戏&#xff0c;从基础了解Cocos2dx-Js的基本实现原理&#xff0c;从创建工程&#xff0c;到各个知识点的梳理。 教程分为上下两讲&#xff1a; 上讲有2个小节&#xff1a; 1&…

在单文档中显示我的第一个对话框

在单文档中显示我的第一个对话框 (2010-04-19 21:19:50) 转载▼标签&#xff1a; it 今天编写了在单文档中创建对话框以及调用对话框的代码&#xff0c;收获很多&#xff1b; 1&#xff1a;OnInitDialog函数的加载问题尤其是在VS上的加载&#xff1b;&#xff08;这一点花了…

主机Window不能访问该虚拟机Linux Samba文件服务提供了一个文件夹

我最近遇到一个问题。虚拟机Linux中间Samba服务常开。主办“\\192.168.229.200” (我的虚拟机Linux址)訪问不了Share文件夹&#xff08;/var/test&#xff09;&#xff0c;并且经过数次的重新启动&#xff0c;检查配置。再重新启动&#xff0c;都没法解决。后来突然想到了主机管…

Linux进程间通信(四) - 共享内存

共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高&#xff0c;因为进程可以直接读写内存&#xff0c;而不需要任何数据的拷贝。对于像管道和消息队列等通信方式&#xff0c;则需要在内核和用户空间进行四次的数据拷贝&#xff0c;而共享内存则只拷贝两次数据&…

jQuery EasyUI使用教程之基本的拖放

2019独角兽企业重金招聘Python工程师标准>>> <jQuery EasyUI最新版下载> 本教程将为你展示如何使HTML元素可拖动。在这个示例中我们将创建3个DIV元素&#xff0c;然后使它们能够被拖动。 查看演示 首先&#xff0c;我们创建3个div元素&#xff1a; < div i…

在Linux中head命令,Linux 中 head 命令实例

原标题&#xff1a;Linux 中 head 命令实例head命令将每个文件的前10行打印到标准输出。对于多个文件&#xff0c;在每个文件前面加上一个给出文件名的头。如果没有文件&#xff0c;或者文件为-&#xff0c;则读取标准输入。如何使用head命令使用head显示/etc/passwd前十行内容…

【原创】Git删除暂存区或版本库中的文件

0 基础 我们知道Git有三大区&#xff08;工作区、暂存区、版本库&#xff09;以及几个状态&#xff08;untracked、unstaged、uncommited&#xff09;&#xff0c;下面只是简述下Git的大概工作流程&#xff0c;详细的可以参见本博客的其他有关Git的文章【链接】。&#xff08;1…

JMS

JMS即Java消息服务&#xff08;Java Message Service&#xff09;应用程序接口是一个Java平台中关于面向消息中间件&#xff08;MOM&#xff09;的API&#xff0c;用于在两个应用程序之间&#xff0c;或分布式系统中发送消息&#xff0c;进行异步通信。Java消息服务是一个与具体…

Android学习四、Android中的Adapter

一、Adapter的介绍 An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to the data items. The Adapter is also responsible for making a View for each item in the data set. 一个Adapter是…

linux select shell,linux之shell编程select和case用法

shell里的select用法&#xff1a;语法&#xff1a;#i/bin/bashselect 变量 in 列表do要执行的语句done举例&#xff1a;#!/bin/bashecho "What is your favourite OS?"select var in "windows" "Linux" "Gnu Hurd" "Free BSD&qu…

Andorid获取状态栏高度

在应用开发中&#xff0c;有时我们需要用代码计算布局的高度&#xff0c;可能需要减去状态栏(status bar)的高度。状态栏高度定义在Android系统尺寸资源中status_bar_height,但这并不是公开可直接使用的&#xff0c;例如像通常使用系统资源那样android.R.dimen.status_bar_heig…

linux 检测蓝牙 rssi,树莓派开发笔记(十一):蓝牙的使用,BlueZ协议(双树莓探测rssi并通过蓝牙互传获取的rssi信号强度)...

若该文为原创文章&#xff0c;转载请注明原文出处长期持续带来更多项目与技术分享&#xff0c;咨询请加QQ:21497936、微信&#xff1a;yangsir198808下一篇&#xff1a;敬请期待…接下来介绍树莓派蓝牙模块的开发&#xff0c;使用的协议为bluez。客户端bob&#xff0c;服务器al…

Android Studio项目整合PullToRefresh的问题记录

PullToRefresh下拉刷新在App中应用非常频繁&#xff0c;然而PullToRefresh是在ADT下开发完成的。如果要将其整合到Android Studio目录下的话颇费周折。前面的文章“Android Studio项目引入外部库注意事项(PullToRefresh)”介绍了如何将PullToRefresh的library引入到项目中。如果…

第 三 十 八 天:Linux 的 LVM 逻 辑 卷 管 理

小Q&#xff1a;书籍是全世界的营养品。生活里没有书籍&#xff0c;就好像没有阳光&#xff1b;智慧里没有书籍&#xff0c;就好像鸟儿没有翅膀。 —— 莎士比亚简介LVM&#xff1a;Logical Volume Manager&#xff08;逻辑卷管理&#xff09;&#xff…

Camera Calibration 相机标定:原理简介(三)

3 绝对圆锥曲线 在进一步了解相机标定前&#xff0c;有必要了解绝对圆锥曲线&#xff08;Absolute Conic&#xff09;这一概念。 对于一个3D空间的点x&#xff0c;其投影空间的坐标为&#xff1a;x~[x1,x2,x3,x4]T。我们定义无穷远处的平面用符号Π∞表示&#xff0c;该平面内的…

C语言判断两字符串同构,c语言实现判断两颗树是否同构

在本题中认为如果两个树左右子树交换可以相同&#xff0c;也被认为是同构树。对应输入格式为&#xff1a;4(总结点数)A - 1B 2 3C - -D - -#include #define Tree int#define Null -1#define MAXSIZE 10struct Node{char Element;Tree Left;Tree Right;}T1[MAXSIZE], T2[MAXSIZ…

C语言常量类型及名称,菜鸟带你入门C语言|基本数据类型之常量

常量在程序中&#xff0c;有些数据是不需要改变的&#xff0c;也是不能改变的&#xff0c;因此&#xff0c;我们把这些不能改变的固定值称为常量。如下图中的“5”、“A”、“Good”&#xff0c;这些在程序执行过程中是一直保持不变的&#xff0c;他们就是常量。printf的作用是…

Android TabHost中实现标签的滚动以及一些TabHost开发的奇怪问题

最近在使用TabHost的时候遇到了一些奇怪的问题&#xff0c;在这里总结分享备忘一下。 首先说一点TabActivity将会被FragmentActivity所替代&#xff0c;但是本文中却是使用的TabActivity。 下面说说本程序能够实现的功能&#xff1a; 实现TabHost中的标题栏能够横向滚动&#x…

tl wn322g linux驱动下载,怎样才能装好tl_wn322G+V2.0版USB无线网卡的Linux驱动

怎样才能装好tl_wn322GV2.0版USB无线网卡的Linux驱动tl_wn322G 2.0版无线网卡采用的是Atheros 的AR9271方案&#xff0c;我尝试了用ndiswrapper-1.55在linux下安装该无线网卡的Windows驱动&#xff0c;安装windows版的驱动时&#xff0c;用ndiswrapper -l &#xff0c;显示为错…