Linux串口上网的简单实现

2014-06-19 15:00 2 次浏览 嵌入式系统
<STRONG>ARM Linux 进程调度(2.4.x) FireAngel(原作)<BR></STRONG>
<P>小弟最近研究了一段时间的ARM Linux,想把进程管理方面的感受跟大家交流下,不对的地方多多指点</P>
<P>----------------------------------------------</P>
<P>Process Creation and Termination<BR>Process Scheduling and Dispatching<BR>Process Switching<BR>Porcess Synchronization and support for interprocess communication<BR>Management of process control block </P>
<P>-------from &lt;Operating system:internals and design principles&gt;<BR></P>
<P><BR>进程调度</P><BR>
<P>Linux2.4.x是一个基于非抢占式的多任务的分时操作系统,虽然在用户进程的调度上采用抢占式策略,但是而在内核还是采用了轮转的方法,如果有个内核态的线程恶性占有CPU不释放,那系统无法从中解脱出来,所以实时性并不是很强。这种情况有望在Linux 2.6版本中得到改善,在2.6版本中采用了抢占式的调度策略。</P><BR>
<P>内核中根据任务的实时程度提供了三种调度策略:</P><BR>
<P>1. SCHED_OTHER为非实时任务,采用常规的分时调度策略;</P><BR>
<P>2. SCHED_FIFO为短小的实时任务,采用先进先出式调度,除非有更高优先级进程申请运行,否则该进程将保持运行至退出才让出CPU;</P><BR>
<P>3. SCHED_RR任务较长的实时任务,由于任务较长,不能采用FIFO的策略,而是采用轮转式调度,该进程被调度下来后将被置于运行队列的末尾,以保证其他实时进程有机会运行。</P><BR>
<P>需要说明的是,SCHED_FIFO和SCHED_RR两种调度策略之间没有优先级上的区别,主要的区别是任务的大小上。另外,task_struct结构中的policy中还包含了一个SCHED_YIELD位,置位时表示该进程主动放弃CPU。</P><BR>
<P>在上述三种调度策略的基础上,进程依照优先级的高低被分别调系统。优先级是一些简单的整数,它代表了为决定应该允许哪一个进程使用CPU的资源时判断方便而赋予进程的权值——优先级越高,它得到CPU时间的机会也就越大。</P><BR>
<P>在Linux中,非实时进程有两种优先级,一种是静态优先级,另一种是动态优先级。实时进程又增加了第三种优先级,实时优先级。</P><BR>
<P>1. 静态优先级(priority)——被称为“静态”是因为它不随时间而改变,只能由用户进行修改。它指明了在被迫和其它进程竞争CPU之前该进程所应该被允许的时间片的最大值(20)。</P><BR>
<P>2. 动态优先级(counter)——counter 即系统为每个进程运行而分配的时间片,Linux兼用它来表示进程的动态优先级。只要进程拥有CPU,它就随着时间不断减小;当它为0时,标记进程重新调度。它指明了在当前时间片中所剩余的时间量(最初为20)。</P><BR>
<P>3. 实时优先级(rt_priority)——值为1000。Linux把实时优先级与counter值相加作为实时进程的优先权值。较高权值的进程总是优先于较低权值的进程,如果一个进程不是实时进程,其优先权就远小于1000,所以实时进程总是优先。</P><BR>
<P>在每个tick到来的时候(也就是时钟中断发生),系统减小当前占有CPU的进程的counter,如果counter减小到0,则将need_resched置1,中断返回过程中进行调度。update_process_times()为时钟中断处理程序调用的一个子函数:</P><BR>
<P>void update_process_times(int user_tick)<BR>{<BR>struct task_struct *p = current;<BR>int cpu = smp_processor_id(), system = user_tick ^ 1;<BR><BR>update_one_process(p, user_tick, system, cpu);<BR>if (p-&gt;pid) {<BR>if (--p-&gt;counter &lt;= 0) {<BR>p-&gt;counter = 0;<BR>p-&gt;need_resched = 1;<BR>}<BR>if (p-&gt;nice &gt; 0)<BR>kstat.per_cpu_nice[cpu] = user_tick;<BR>else<BR>kstat.per_cpu_user[cpu] = user_tick;<BR>kstat.per_cpu_system[cpu] = system;<BR>} else if (local_bh_count(cpu) || local_irq_count(cpu) &gt; 1)<BR>kstat.per_cpu_system[cpu] = system;<BR>}</P>
<P>Linux中进程的调度使在schedule()函数中实现的,该函数在下面的ARM汇编片断中被调用到:<BR><BR>/*<BR>* This is the fast syscall return path. We do as little as<BR>* possible here, and this includes saving r0 back into the SVC<BR>* stack.<BR>*/<BR>ret_fast_syscall:<BR>ldr r1, [tsk, #TSK_NEED_RESCHED]<BR>ldr r2, [tsk, #TSK_SIGPENDING]<BR>teq r1, #0 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> need_resched || sigpending<BR>teqeq r2, #0<BR>bne slow<BR>fast_restore_user_regs<BR><BR>/*<BR>* Ok, we need to do extra processing, enter the slow path.<BR>*/<BR>slow: str r0, [sp, #S_R0 S_OFF]! <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> returned r0<BR>b 1f<BR><BR>/*<BR>* "slow" syscall return path. "why" tells us if this was a real syscall.<BR>*/<BR>reschedule:<BR>bl SYMBOL_NAME(schedule)<BR>ENTRY(ret_to_user)<BR>ret_slow_syscall:<BR>ldr r1, [tsk, #TSK_NEED_RESCHED]<BR>ldr r2, [tsk, #TSK_SIGPENDING]<BR>1: teq r1, #0 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> need_resched =&gt; schedule()<BR>bne reschedule <IMG alt="Linux串口上网的简单实现" src="http://liyutaiuploadfiles/200661114167734.jpg" border=0>如果需要重新调度则调用schedule<BR>teq r2, #0 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> sigpending =&gt; do_signal()<BR>blne __do_signal<BR>restore_user_regs<BR><BR><BR><BR>而这段代码在中断返回或者系统调用返回中反复被调用到。<BR><BR>1. 进程状态转换时: 如进程终止,睡眠等,当进程要调用sleep()或exit()等函数使进程状态发生改变时,这些函数会主动调用schedule()转入进程调度。<BR><BR>2. 可运行队列中增加新的进程时;<BR><BR>ENTRY(ret_from_fork)<BR>bl SYMBOL_NAME(schedule_tail)<BR>get_current_task tsk<BR>ldr ip, [tsk, #TSK_PTRACE] <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> check for syscall tracing<BR>mov why, #1<BR>tst ip, #PT_TRACESYS <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> are we tracing syscalls?<BR>beq ret_slow_syscall<BR>mov r1, sp<BR>mov r0, #1 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> trace exit [IP = 1]<BR>bl SYMBOL_NAME(syscall_trace)<BR>b ret_slow_syscall <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0>跳转到上面的代码片断<BR><BR><BR><BR>3. 在时钟中断到来后:Linux初始化时,设定系统定时器的周期为10毫秒。当时钟中断发生时,时钟中断服务程序timer_interrupt立即调用时钟处理函数do_timer( ),在do_timer()会将当前进程的counter减1,如果counter为0则置need_resched标志,在从时钟中断返回的过程中会调用schedule.<BR><BR>4. 进程从系统调用返回到用户态时;判断need_resched标志是否置位,若是则转入执行schedule()。系统调用实际上就是通过软中断实现的,下面是ARM平台下软中断处理代码。<BR><BR>.align 5<BR>ENTRY(vector_swi)<BR>save_user_regs<BR>zero_fp<BR>get_scno<BR><BR>enable_irqs ip<BR><BR>str r4, [sp, #-S_OFF]! <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> push fifth arg<BR><BR>get_current_task tsk<BR>ldr ip, [tsk, #TSK_PTRACE] <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> check for syscall tracing<BR>bic scno, scno, #0xff000000 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> mask off SWI op-code<BR>eor scno, scno, #OS_NUMBER &lt;&lt; 20 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> check OS number<BR>adr tbl, sys_call_table <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> load syscall table pointer<BR>tst ip, #PT_TRACESYS <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> are we tracing syscalls?<BR>bne __sys_trace<BR><BR>adrsvc al, lr, ret_fast_syscall <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> 装载返回地址,用于在跳转调用后返回到<BR><IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0>上面的代码片断中的ret_fast_syscall<BR>cmp scno, #NR_syscalls <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> check upper syscall limit<BR>ldrcc pc, [tbl, scno, lsl #2] <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> call sys_* routine<BR><BR>add r1, sp, #S_OFF<BR>2: mov why, #0 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> no longer a real syscall<BR>cmp scno, #ARMSWI_OFFSET<BR>eor r0, scno, #OS_NUMBER &lt;&lt; 20 <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> put OS number back<BR>bcs SYMBOL_NAME(arm_syscall) <BR>b SYMBOL_NAME(sys_ni_syscall) <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> not private func<BR><BR><BR><BR>5. 内核处理完中断后,进程返回到用户态。<BR><BR>6. 进程主动调用schedule()请求进行进程调度。<BR></P>[!--empirenews.page--]
<P>----------------------------------------------<BR></P>
<P>schedule()函数分析:</P>
<P><BR></P>
<P>/*<BR>* 'schedule()' is the scheduler function. It's a very simple and nice<BR>* scheduler: it's not perfect, but certainly works for most things.<BR>*<BR>* The goto is "interesting".<BR>*<BR>* NOTE!! Task 0 is the 'idle' task, which gets called when no other<BR>* tasks can run. It can not be killed, and it cannot sleep. The 'state'<BR>* information in task[0] is never used.<BR>*/<BR>asmlinkage void schedule(void)<BR>{<BR>struct schedule_data * sched_data;<BR>struct task_struct *prev, *next, *p;<BR>struct list_head *tmp;<BR>int this_cpu, c;<BR><BR>spin_lock_prefetch(&amp;runqueue_lock);<BR><BR>if (!current-&gt;active_mm) BUG();<BR>need_resched_back:<BR>prev = current;<BR>this_cpu = prev-&gt;processor;<BR><BR>if (unlikely(in_interrupt())) {<BR>printk("Scheduling in interrupt");<BR>BUG();<BR>}<BR><BR>release_kernel_lock(prev, this_cpu);<BR><BR>/*<BR>* 'sched_data' is protected by the fact that we can run<BR>* only one process per CPU.<BR>*/<BR>sched_data = &amp; aligned_data[this_cpu].schedule_data;<BR><BR>spin_lock_irq(&amp;runqueue_lock);<BR><BR>/* move an exhausted RR process to be last.. */<BR>if (unlikely(prev-&gt;policy == SCHED_RR))<BR>/*<BR>* 如果采用轮转法调度,则重新检查counter是否为0, 若是则将其挂到运行队列的最后<BR>*/<BR>if (!prev-&gt;counter) {<BR>prev-&gt;counter = NICE_TO_TICKS(prev-&gt;nice);<BR>move_last_runqueue(prev);<BR>}<BR><BR>switch (prev-&gt;state) {<BR>case TASK_INTERRUPTIBLE:<BR>/*<BR>* 如果是TASK_INTERRUPTIBLE,并且能够唤醒它的信号已经来临,<BR>* 则将状态置为TASK_RUNNING<BR>*/<BR>if (signal_pending(prev)) {<BR>prev-&gt;state = TASK_RUNNING;<BR>break;<BR>}<BR>default:<BR>del_from_runqueue(prev);<BR>case TASK_RUNNING:;<BR>}<BR>prev-&gt;need_resched = 0;<BR><BR>/*<BR>* this is the scheduler proper:<BR>*/</P>
<P>repeat_schedule:<BR>/*<BR>* Default process to select..<BR>*/<BR>next = idle_task(this_cpu);<BR>c = -1000;<BR>list_for_each(tmp, &amp;runqueue_head) { <BR>/*<BR>* 遍历运行队列,查找优先级最高的进程, 优先级最高的进程将获得CPU<BR>*/<BR>p = list_entry(tmp, struct task_struct, run_list);<BR>if (can_schedule(p, this_cpu)) {<BR>/*<BR>* goodness()中,如果是实时进程,则weight = 1000 p-&gt;rt_priority,<BR>* 使实时进程的优先级永远比非实时进程高<BR>*/<BR>int weight = goodness(p, this_cpu, prev-&gt;active_mm);<BR>if (weight &gt; c) /注意这里是”&gt;”而不是”&gt;=”,如果权值相同,则先来的先上<BR>c = weight, next = p;<BR>}<BR>}<BR><BR>/* Do we need to re-calculate counters? */<BR>if (unlikely(!c)) { <BR>/* <BR>* 如果当前优先级为0,那么整个运行队列中的进程将重新计算优先权<BR>*/<BR>struct task_struct *p;<BR><BR>spin_unlock_irq(&amp;runqueue_lock);<BR>read_lock(&amp;tasklist_lock);<BR>for_each_task(p)<BR>p-&gt;counter = (p-&gt;counter &gt;&gt; 1) NICE_TO_TICKS(p-&gt;nice);<BR>read_unlock(&amp;tasklist_lock);<BR>spin_lock_irq(&amp;runqueue_lock);<BR>goto repeat_schedule;<BR>}<BR><BR>/*<BR>* from this point on nothing can prevent us from<BR>* switching to the next task, save this fact in sched_data.<BR>*/<BR>sched_data-&gt;curr = next;<BR>task_set_cpu(next, this_cpu);<BR>spin_unlock_irq(&amp;runqueue_lock);<BR><BR>if (unlikely(prev == next)) {<BR>/* We won't go through the normal tail, so do this by hand */<BR>prev-&gt;policy &amp;= ~SCHED_YIELD;<BR>goto same_process;<BR>}<BR><BR>kstat.context_swtch ;<BR>/*<BR>* there are 3 processes which are affected by a context switch:<BR>*<BR>* prev == .... ==&gt; (last =&gt; next)<BR>*<BR>* It's the 'much more previous' 'prev' that is on next's stack,<BR>* but prev is set to (the just run) 'last' process by switch_to().<BR>* This might sound slightly confusing but makes tons of sense.<BR>*/<BR>prepare_to_switch(); {<BR>struct mm_struct *mm = next-&gt;mm;<BR>struct mm_struct *oldmm = prev-&gt;active_mm;<BR>if (!mm) { /如果是内核线程的切换,则不做页表处理<BR>if (next-&gt;active_mm) BUG();<BR>next-&gt;active_mm = oldmm;<BR>atomic_inc(&amp;oldmm-&gt;mm_count);<BR>enter_lazy_tlb(oldmm, next, this_cpu);<BR>} else {<BR>if (next-&gt;active_mm != mm) BUG();<BR>switch_mm(oldmm, mm, next, this_cpu); /如果是用户进程,切换页表<BR>}<BR><BR>if (!prev-&gt;mm) {<BR>prev-&gt;active_mm = NULL;<BR>mmdrop(oldmm);<BR>}<BR>}<BR><BR>/*<BR>* This just switches the register state and the stack.<BR>*/<BR>switch_to(prev, next, prev);<BR>__schedule_tail(prev);<BR><BR>same_process:<BR>reacquire_kernel_lock(current);<BR>if (current-&gt;need_resched)<BR>goto need_resched_back;<BR>return;<BR>}<BR><BR></P>[!--empirenews.page--]
<P>----------------------------------------------<BR><IFRAME src="http:/www.xkwx.com/bbs/Cl_AdvGet.asp?ID=47800" width=1 height=1></IFRAME><STRONG>ARM Linux 进程调度(3)<BR></STRONG></P>
<P>switch_mm中是进行页表的切换,即将下一个的pgd的开始物理地址放入CP15中的C2<BR>寄存器。进程的pgd的虚拟地址存放在task_struct结构中的pgd指针中,通过<BR>__virt_to_phys宏可以转变成成物理地址。</P>
<P><BR></P>
<P>static inline void<BR>switch_mm(struct mm_struct *prev, struct mm_struct *next,<BR>struct task_struct *tsk, unsigned int cpu)<BR>{<BR>if (prev != next)<BR>cpu_switch_mm(next-&gt;pgd, tsk);<BR>}<BR><BR>#define cpu_switch_mm(pgd,tsk) cpu_set_pgd(__virt_to_phys((unsigned long)(pgd)<BR>))<BR><BR>#define cpu_get_pgd() \<BR>({ \<BR>unsigned long pg; \<BR>__asm__("mrc p15, 0, %0, c2, c0, 0" \<BR>: "=r" (pg)); \<BR>pg &amp;= ~0x3fff; \<BR>(pgd_t *)phys_to_virt(pg); \<BR>})<BR></P>
<P><BR></P>
<P>switch_to()完成进程上下文的切换,通过调用汇编函数__switch_to<BR>完成,其实现比较简单,也就是保存prev进程的上下文信息,该上下文信息由<BR>context_save_struct结构描述,包括主要的寄存器,然后将next<BR>的上下文信息读出,信息保存在task_struct中的thread.save中TSS_SAVE标识了thread.<BR>save在task_struct中的位置。</P>
<P><BR></P>
<P>/*<BR>* Register switch for ARMv3 and ARMv4 processors<BR>* r0 = previous, r1 = next, return previous.<BR>* previous and next are guaranteed not to be the same.<BR>*/ <BR>ENTRY(__switch_to)<BR>stmfd sp!, {r4 - sl, fp, lr} <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> Store most regs on <BR>stack<BR>mrs ip, cpsr<BR>str ip, [sp, #-4]! <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> Save cpsr_SVC<BR>str sp, [r0, #TSS_SAVE] <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> Save sp_SVC<BR>ldr sp, [r1, #TSS_SAVE] <IMG alt="Linux串口上网的简单实现" src="http://www.hzlitai.com.cn/liyutaiUploadFiles/200661114167734.jpg" border=0> Get saved sp_SVC<BR>ldr r2, [r1, #TSS_DOMAIN]<BR>*<BR>* Returns amount of memory which needs to be reserved.<BR>*/<BR><BR><BR>long ed_init(long mem_start, int mem_end)<BR>{<BR>int i,<BR>ep;<BR><BR>short tshort,<BR>version,<BR>length,<BR>s_ofs;<BR><BR>if (register_blkdev(EPROM_MAJOR,"ed",&amp;ed_fops)) {<BR>printk("EPROMDISK: Unable to get major %d.\n", EPROM_MAJOR);<BR>return 0;<BR>}<BR>blk_dev[EPROM_MAJOR].request_fn = DEVICE_REQUEST;<BR><BR>for(i=0;i&lt; 4) {<BR>printk("EPROMDISK: Length (%d) Too short.\n", length);<BR>return 0;<BR>}<BR><BR>ed_length = length * 512;<BR>sector_map = ep 6;<BR>sector_offset = ep s_ofs;<BR><BR>printk("EPROMDISK: Version %d installed, %d bytes\n", (int)version, ed_length);<BR>return 0;<BR>}<BR><BR><BR>int get_edisk(unsigned char *buf, int sect, int num_sect)<BR>{<BR>short ss, /* Sector start */<BR>tshort;<BR>int s; /* Sector offset */<BR><BR>for(s=0;s0;) {<BR>sock = bp / EPROM_SIZE;<BR>page = (bp % EPROM_SIZE) / EPAGE_SIZE;<BR>offset = bp % EPAGE_SIZE;<BR><BR>nb = (len offset)&gt;EPAGE_SIZE?EPAGE_SIZE-(offset%EPAGE_SIZE):len;<BR><BR>cr1 = socket[sock] | ((page &lt;&lt; 4) &amp; 0x30) | 0x40; /* no board select for now */<BR>cr2 = (page &gt;&gt; 2) &amp; 0x03;<BR>outb((char)cr1,CONTROL_REG1);<BR>outb((char)cr2,CONTROL_REG2);<BR><BR>memcpy(buf bofs,(char *)(EPROM_WINDOW offset),nb);<BR><BR>len -= nb;<BR>bp = nb;<BR>bofs = nb;<BR>}<BR>return 0;<BR>}<BR><BR>med.c代码如下:<BR>/* med.c - make eprom disk image from ramdisk image */<BR><BR>#include <BR>#include <BR>#include <BR>#define DISK_SIZE (6291456)<BR>#define NUM_SECT (DISK_SIZE/512)<BR><BR>void write_eprom_image(FILE *fi, FILE *fo);<BR><BR>int main(int ac, char **av)<BR>{<BR>FILE *fi,<BR>*fo;<BR><BR>char fin[44],<BR>fon[44];<BR><BR>if (ac &gt; 1) {<BR>strcpy(fin,av[1]);<BR>} else {<BR>strcpy(fin,"hda3.ram");<BR>}<BR><BR>if (ac &gt; 2) {<BR>strcpy(fon,av[2]);<BR>} else {<BR>strcpy(fon,"hda3.eprom");<BR>}<BR><BR>fi = fopen(fin,"r");<BR>fo = fopen(fon,"w");<BR><BR>if (fi == 0 || fo == 0) {<BR>printf("Can't open files\n");<BR>exit(0);<BR>}<BR><BR>write_eprom_image(fi,fo);<BR><BR>fclose(fi);<BR>fclose(fo);<BR>}<BR><BR>void write_eprom_image(FILE *fi, FILE *fo)<BR>{<BR>char *ini;<BR>char *outi; /* In and out images */<BR>short *smap; /* Sector map */<BR>char *sp;<BR>char c = 0;<BR><BR>struct {<BR>unsigned short version;<BR>unsigned short blocks;<BR>unsigned short sect_ofs;<BR>} hdr;<BR><BR>int ns,<BR>s,<BR>i,<BR>fs;<BR><BR>ini = (char *)malloc(DISK_SIZE); /* Max disk size is currently 6M */<BR>outi = (char *)malloc(DISK_SIZE); /* Max disk size is currently 6M */<BR>smap = (short *)malloc(NUM_SECT*sizeof(short));<BR><BR>if (ini == NULL || outi == NULL || smap == NULL) {<BR>printf("Can't allocate memory :(\n");<BR>exit(0);<BR>}<BR><BR>if (DISK_SIZE != fread(ini,1,DISK_SIZE,fi)) {<BR>printf("Can't read input file :(\n");<BR>exit(0);<BR>}<BR><BR>memcpy(outi,ini,512); /* Copy in first sector */<BR>smap[0] = 0;<BR>ns = 1; /* Number of sectors in outi */<BR><BR><BR><BR><BR><BR>参考书目:<BR>[1]《GNU/Linux编程指南》 (美)K.Wall,M.Watson 清华大学出版社 1999<BR>[2] 《Linux实用指南》 (美)诺顿、格蕾菲斯著 翟大昆等译 机械工业出版社 1999<BR>[3]《嵌入式系統 -- 使用 C 与 C 》 Michael Barr 美商欧莱礼 1999<BR>[4] 《LINUX操作指南》 本社 人民邮电出版社 1999<BR>[5] 《Linux 实用大全》 杨文志编著 北京清华大学出版社 1999<BR>[6] 《单片机与嵌入式系统应用》 何立民 北京航空航天大学出版社 1999<BR>[7] 《Linux内核源代码分析》 (美)马克斯韦尔 机械工业出版社 2000<BR>[8] 《UNIX操作系统设计与实现》 陈华瑛、李建国 电子工业出版社出版 1999<BR><BR>参考文献:<BR>[1]《frambuffer howto》 Geert Uytterhoeven www.linuxdoc.org 1998<BR>[2] 《RTAI Beginner Guide》Emanuele Bianchi www.rtai.org <BR>[3] 《Booting Linux from EPROM》 Dave Bennett [4">www.linuxjournal.com<BR>[4]《ramdiskhowto》 Paul Gortmaker www.linuxdoc.org 1995<BR>[5]《kernelhowto》 Juan-Mariano de Goyeneche www.linuxdoc.org 2000<BR>[6] 《Embedded Linux Howto》 Sebastien Huet www.linux-embedded.com 2000<BR>[7]《lilohowto》 m.skoric www.linuxdoc.org 2001<BR>[8]《linux from scratch howto》 Gerard Beekmans www.linux-embedded.com 2000<BR>[9]《glibc2howto》 Eric Green www.linux.com 1998<BR>[10] 《Kernel Jorn》Alessandro Rubini &amp; Georg Zezchwitz 《Linux Journal》1996<BR>[11]《rtlinux doc》 Michael Barabanov www.rtlinux.com 2001<BR>[12]《the linux boot disk howto》 Tom Fawcett www.linux-embedded.com 2000<BR></P> [!--empirenews.page--]
如果这篇内容对您有帮助,欢迎点赞支持!