八皇后问题 c语言代码,用代码写一下,第一题

当前访客身份:游客 [
当前位置:
用C语言“写”操作系统
操作系统并没有那么难实现
道&可道&非常道&名&可名&非常名
&&&&&&&&&&&&&&&&&&&&&&&&---老子
这是一句富有哲理的话,我斗胆把它放到我的这篇文章的开始,你可以对它很是留意,也可以忽视它。
“Compilation&can&invole&up&to&four&stages:Preprocessing,compilation&proper,assembly&and&linking,always&in&that&order.”
---Quote&From&GNU&Compilers’&Documents
根据上述对高级语言编译器(下简称为“编译器”)行为的描述,我们可以将编译器的编译过程庞统地分为以下三个阶段:
Step&1:反编译(),即将转化为功能等价的汇编代码;
Step&2:装配(),将助记符(汇编)代码转化成对应机器二进制执行代码;
Step&3:连接(),连接一些外部函数调用并整理成。
相对的来讲,()等汇编器对汇编代码的处理过程要简单点。
但是,有一个小问题,如果一个源文件里面没有进行库函数的调用而且没有程序入口点,此时,能对它最那些处理呢?(请认真思考,这个问题对于我们的目标很重要。)
&&下面谈我们的开发平台:
&&CPU:Intel80386&&Emulation&Software:Bochs-2.1.1
&&Compilers:Turbo&C&2.0&&Netwide&Assembler&2.07&(NASM&2.07)
&&第一步:
&&Assumption&1:存在一个函数(),它和中函数功能完全相同。
&&Assumption&2:在下列函数被调用之前,≡,≡,≡。&&&
&&我们的文件的代码如下:
void&putch(char&ch)
void&printf(char&*&pt);
void&proc1()
&&char&*&pt=&&&Haha!&^_^&MISSION&SUCCESS!\n&;
&&char&i=0;
&&for(;i&100;i++)
&&&&pt[0]=i/10+'0';
&&&&pt[1]=i%10+'0';
&&&&printf(pt);
void&printf(char&*&pt)
&&char&i=0;
&&char&len=0;
&&for(i=0;pt[i]!=0;i++,len++);
&&for(i=0;i&i++)
&&&&if(pt[i]==10)
&&&&&&putch(10);
&&&&&&putch(13);
&&&&&&putch(pt[i]);
&&请仔细读完代码。
&&既然你已经读完了这段简单的代码,如果我们举出的两个假设成立的话,那么,函数proc1被调用之后执行结果是什么,聪明的你一定能思考到!
&&用Turbo&C&2.0的OS&Shell输出C.C的汇编代码:TCC&–S&C.C。(8086指令集,Model:Tiny,别忘了我们的环境是实模式下的386,如有问题请自己解决,然后再向下读。)
&&如下是(即我们的输出)的代码内容:
ifndef ??version
?debug macro
?debug S&&c.c&
_TEXT segment byte&public&'CODE'
DGROUP group _DATA,_BSS
assume cs:_TEXT,ds:DGROUP,ss:DGROUP
_TEXT ends
_DATA segment&word&public&'DATA'
d@ label byte
label word
_DATA ends
_BSS segment&word&public&'BSS'
b@ label byte
label word
?debug C&E96DE63
_TEXT segment byte&public&'CODE'
; ?debug L&1
_putch proc near
; ?debug L&2
_putch endp
; ?debug L&4
_proc1 proc near
; ?debug L&6
mov si,offset&DGROUP:s@
; ?debug L&7
mov byte&ptr&[bp-1],0
jmp short&@6
; ?debug L&10
mov al,byte&ptr&[bp-1]
mov byte&ptr&[si],al
; ?debug L&11
mov al,byte&ptr&[bp-1]
mov byte&ptr&[si+1],dl
; ?debug L&12
call near&ptr&_printf
; ?debug L&8
inc byte&ptr&[bp-1]
; ?debug L&8
cmp byte&ptr&[bp-1],100
; ?debug L&14
_proc1 endp
; ?debug L&15
_printf proc near
mov si,word&ptr&[bp+4]
; ?debug L&17
mov byte&ptr&[bp-2],0
; ?debug L&18
mov byte&ptr&[bp-1],0
; ?debug L&20
mov byte&ptr&[bp-2],0
jmp short&
inc byte&ptr&[bp-2]
inc byte&ptr&[bp-1]
mov al,byte&ptr&[bp-2]
cmp byte&ptr&[bx+si],0
; ?debug L&22
mov byte&ptr&[bp-2],0
jmp short&@15
; ?debug L&24
mov al,byte&ptr&[bp-2]
cmp byte&ptr&[bx+si],10
; ?debug L&26
call near&ptr&_putch
; ?debug L&27
call near&ptr&_putch
; ?debug L&28
jmp short&
; ?debug L&31
mov al,byte&ptr&[bp-2]
push word&ptr&[bx+si]
call near&ptr&_putch
inc byte&ptr&[bp-2]
mov al,byte&ptr&[bp-2]
cmp al,byte&ptr&[bp-1]
; ?debug L&34
_printf endp
_TEXT ends
?debug C&E9
_DATA segment&word&public&'DATA'
s@ label byte
_DATA ends
_TEXT segment byte&public&'CODE'
_TEXT ends
public _printf
public _putch
public _proc1
请仔细读完代码。
下面是什么?请自己看。
The&C&calling&convention&in&16-bit&programs&is&as&follows.&In&the&following&description,&the&words&caller&and
callee&are&used&to&denote&the&function&doing&the&calling&and&the&function&which&gets&called.
o&The&caller&pushes&the&function’s&parameters&on&the&stack,&one&after&another,&in&reverse&order&(right&to&left,&so
that&the&first&argument&specified&to&the&function&is&pushed&last).
o&The&caller&then&executes&a&CALL&instruction&to&pass&control&to&the&callee.&This&CALL&is&either&near&or&far
depending&on&the&memory&model.
o&The&callee&receives&control,&and&typically&(although&this&is&not&actually&necessary,&in&functions&which&do&not
need&to&access&their&parameters)&starts&by&saving&the&value&of&SP&in&BP&so&as&to&be&able&to&use&BP&as&a&base
pointer&to&find&its&parameters&on&the&stack.&However,&the&caller&was&probably&doing&this&too,&so&part&of&the
calling&convention&states&that&BP&must&be&preserved&by&any&C&function.&Hence&the&callee,&if&it&is&going&to&set
up&BP&as&a&frame&pointer,&must&push&the&previous&value&first.
o&The&callee&may&then&access&its&parameters&relative&to&BP.&The&word&at&[BP]&holds&the&previous&value&of&BP
as&it&was&&the&next&word,&at&[BP+2],&holds&the&offset&part&of&the&return&address,&pushed&implicitly
by&CALL.&In&a&small-model&(near)&function,&the&parameters&start&after&that,&at&[BP+4];&in&a&large-model
(far)&function,&the&segment&part&of&the&return&address&lives&at&[BP+4],&and&the&parameters&begin&at
[BP+6].&The&leftmost&parameter&of&the&function,&since&it&was&pushed&last,&is&accessible&at&this&offset&from
BP;&the&others&follow,&at&successively&greater&offsets.&Thus,&in&a&function&such&as&printf&which&takes&a
variable&number&of&parameters,&the&pushing&of&the&parameters&in&reverse&order&means&that&the&function
knows&where&to&find&its&first&parameter,&which&tells&it&the&number&and&type&of&the&remaining&ones.
o&The&callee&may&also&wish&to&decrease&SP&further,&so&as&to&allocate&space&on&the&stack&for&local&variables,
which&will&then&be&accessible&at&negative&offsets&from&BP.
o&The&callee,&if&it&wishes&to&return&a&value&to&the&caller,&should&leave&the&value&in&AL,&AX&or&DX:AX
depending&on&the&size&of&the&value.&Floating-point&results&are&sometimes&(depending&on&the&compiler)
returned&in&ST0.
o&Once&the&callee&has&finished&processing,&it&restores&SP&from&BP&if&it&had&allocated&local&stack&space,&then
pops&the&previous&value&of&BP,&and&returns&via&RETN&or&RETF&depending&on&memory&model.
o&When&the&caller&regains&control&from&the&callee,&the&function&parameters&are&still&on&the&stack,&so&it&typically
adds&an&immediate&constant&to&SP&to&remove&them&(instead&of&executing&a&number&of&slow&POP
instructions).&Thus,&if&a&function&is&accidentally&called&with&the&wrong&number&of&parameters&due&to&a
prototype&mismatch,&the&stack&will&still&be&returned&to&a&sensible&state&since&the&caller,&which&knows&how
many&parameters&it&pushed,&does&the&removing.
---Quote&From&NASM&Documents
&&既然你已经读完了这段看似复杂其实特别简单的的代码,你一定明白了很多很多事情,例如为什么我们的编译器是它们两个,C语言和汇编的关系原来如此等等。
&&为了与NASM格式彻底兼容,我们必须再做一些小改动,改动后的C.asm文件命名为C_Processed.asm,具体代码内容如下:
&&mov&&bp,sp
mov byte&&[bp-1],0
jmp short&@6
mov al,byte&&[bp-1]
mov byte&&[si],al
mov al,byte&&[bp-1]
mov byte&&[si+1],dl
call near&&_printf
inc byte&&[bp-1]
cmp byte&&[bp-1],100
mov si,word&&[bp+4]
mov byte&&[bp-2],0
mov byte&&[bp-1],0
mov byte&&[bp-2],0
jmp short&
inc byte&&[bp-2]
inc byte&&[bp-1]
mov al,byte&&[bp-2]
cmp byte&&[bx+si],0
mov byte&&[bp-2],0
jmp short&@15
mov al,byte&&[bp-2]
cmp byte&&[bx+si],10
call near&&_putch
call near&&_putch
jmp short&
mov al,byte&&[bp-2]
push word&&[bx+si]
call near&&_putch
inc byte&&[bp-2]
mov al,byte&&[bp-2]
cmp al,byte&&[bp-1]
&&&&&&&db 32
请仔细读完以上代码,观察有哪些改动以及原因。
&&现在,最主要的问题就是C语言与汇编语言的接口函数_putch(char&ch)的实现,这个过程需要足够的重视,这是目前为止最关键的一步。
&&接口如下:
&&_putch:&&;&1&para&in&
&&mov&&bp,sp
&&mov&&ax,[bp+4]&&
&&mov&&ah,0eh
&&mov&&bx,00h
&&int&&10h
&&mov&&sp,bp
&&下面是我们真正的函数入口点!
&&pre.asm文件的代码内容如下:
&&CPU&8086
org&07c00h
jmp&BootBegin&
times&100&db&0
Stack_Top:
BootBegin:
&&mov&&ax,0000h
&&mov&&ss,ax
&&mov&&es,ax
&&mov&&ds,ax
&&mov&&ax,Stack_Top
&&mov&&sp,ax
&&call&CGAModeSet
&&call&ClearScreen
&&mov&&dh,0&&&&;para&1
&&mov&&dl,0&&&&;para&2
&&call&SetCursor
&&call&_proc1
CGAModeSet:
&&mov&&ah,0
&&mov&&al,3
&&int&&10h
ClearScreen:
&&mov&&ax,0600h
&&mov&&bh,7
&&mov&&cx,0
&&mov&&dx,184fh
&&int&&10h
SetCursor:&&;dh,dl&(in)
&&mov&&ah,2
&&mov&&bh,0
&&int&&10h
_putch:&&;&1&para&in&&&
&&mov&&bp,sp
&&mov&&ax,[bp+4]&&
&&mov&&ah,0eh
&&mov&&bx,00h
&&int&&10h
&&mov&&sp,bp
times&510-($-$$)&db&0
dw&&&&0xaa55
&&整合,得到share.asm,代码内容如下:
&&CPU&8086
org&07c00h
jmp&BootBegin&
times&100&db&0
Stack_Top:
BootBegin:
&&mov&&ax,0000h
&&mov&&ss,ax
&&mov&&es,ax
&&mov&&ds,ax
&&mov&&ax,Stack_Top
&&mov&&sp,ax
&&call&CGAModeSet
&&call&ClearScreen
&&mov&&dh,0&&&&;para&1
&&mov&&dl,0&&&&;para&2
&&call&SetCursor
&&call&_proc1
CGAModeSet:
&&mov&&ah,0
&&mov&&al,3
&&int&&10h
ClearScreen:
&&mov&&ax,0600h
&&mov&&bh,7
&&mov&&cx,0
&&mov&&dx,184fh
&&int&&10h
SetCursor:&&;dh&dl&(in)
&&mov&&ah,2
&&mov&&bh,0
&&int&&10h
_putch:&&;&1&in&&;C&Model&Fnc
&&mov&&bp,sp
&&mov&&ax,[bp+4]&&
&&mov&&ah,0eh
&&mov&&bx,00h
&&int&&10h
&&mov&&sp,bp
mov byte&&[bp-1],0
jmp short&@6
mov al,byte&&[bp-1]
mov byte&&[si],al
mov al,byte&&[bp-1]
mov byte&&[si+1],dl
call near&&_printf
inc byte&&[bp-1]
cmp byte&&[bp-1],100
mov si,word&&[bp+4]
mov byte&&[bp-2],0
mov byte&&[bp-1],0
mov byte&&[bp-2],0
jmp short&
inc byte&&[bp-2]
inc byte&&[bp-1]
mov al,byte&&[bp-2]
cmp byte&&[bx+si],0
mov byte&&[bp-2],0
jmp short&@15
mov al,byte&&[bp-2]
cmp byte&&[bx+si],10
call near&&_putch
call near&&_putch
jmp short&
mov al,byte&&[bp-2]
push word&&[bx+si]
call near&&_putch
inc byte&&[bp-2]
mov al,byte&&[bp-2]
cmp al,byte&&[bp-1]
&&&&&&&db 32
times&510-($-$$)&db&0
dw&&&&0xaa55
&&用NASM编译得到BinaryCode,写入boot软盘映像,启动Bochs仿真,和你的预想一样吗?
&&仿真结果:
&&最后,我们还遗留下了一个最严重的问题,那就是Turbo&C&2.0&反汇编的行为准则,我查阅了很多资料,无果,没有一本书或者一个网站能给我一点信息。我经过Bochs仿真进行试验,发现proc1()函数可以抽象为一个输出y=f[x,y,z&.&.&.]的系统,这个系统一旦被激活(即&call&_proc1),它的输出只受它的输入参数(x,y,z&.&.&.)唯一控制(当然这种情况之下并不考虑静态变量,全局变量以及对时间有要求的函数),而且它可以随意使用eax,ebx,ecx和edx,以及在不对全局进程具有任何影响的情况之下任意使用其他寄存器。
&&不知哪位兄弟能提供一些关于compilers‘&conventions&的资料,小弟在这儿先谢过了!
&&1.可能有些朋友对我在句子中夹杂一些英文术语很是反感,但是我也很无奈,因为,在很多情况之下,一些英文术语翻译成的汉语词汇,不但没有起到解释说明的作用,反而相比于英文本身更加让学习者们感到困惑。比如上文中的“compilers‘&conventions”,我实在不知道用汉语中的那个词汇能像英文那样表达清楚我的意思。
&&2.我要解释一下题目了,“用C语言‘写’操作系统”,各位现在明白了吧,对于实模式下的编程,C只是一种深度数据抽象的工具,根本还是汇编,哈哈!对于32位的代码,自然要用GCC的ELF来实现了!那只会更让人激动!
&&3.我连续花了大约四天的时间来寻找是否有一种编译器能直接联合汇编与C来编写BootSector和加载软盘,DVD,或者硬盘的16位开发环境,遗憾的是!这似乎比去追一个漂亮的女孩子还要难!哈哈!(不管是配合NASM,还是MASM,在连接生成.COM文件时,TLINK&总是报错,而且dos下的.EXE文件的格式文档似乎从来没有被链接入过互联网一样)最后,我终于突然想到了这样的一种方法,让我欣喜若狂的是,TCC输出的反汇编格式是MASM的!这样就能很容易的被重新利用!然后我就想到了我在寻找资料的过程中,遇到了很多和我具有相同困惑的朋友,既然我这边有了一点小小的进展,理所当然的要拿出来与大家一起分享了,即使是个看似愚蠢的方法,因为&CSDN是个卧虎藏龙的地方!请相信!所以我在发帖之前就准备接受大家的批评了,不过最好还是,能给点建议,让我这个迷途的小孩子走入大道,和高手们一起学习和进步!所以,我要向高手兄弟们,致敬!(今天有点激动了,失态了,哈哈哈!)
&&4.“操作系统并没有那么难实现”,一切都是有迹可循的。“WindowsVista全部代码超过7亿行,OS/360的开发用了大约5000人年”等等还有一些外行人的危言耸听使很多对操作系统开发有雄心壮志的年轻朋友们对“她”望而生畏,其实“她”很迷人!也渴望被人了解!呵呵!但是你不是也知道Pro.Andrew&S.Tanenbaum&独自开发的Minix操作系统早期只有12000行么?而且由她间接影响了Linux的产生,在计算机科学史上也成了一段佳话!OS/360之所以庞大且Bug丛生,我就引用Pro.Andrew&S.Tanenbaum&的话来解释吧,“&&&.Thus&OS/360&is&a&ambitious&system&to&be&built&that&will&fall!而&WindowsVista之所以那么庞大,一个主要原因是因为他立足于商业,是为了盈利,是面向计算机“盲人”们的,所以有了众多庞大且复杂的GUI,总之一句话,微软是为了钱!(似乎有点绝对,哈哈!)而Minix和Linux是为了人类的进步!(这里可能有点偏激,所以请读者达意即可,不必一般见识。)
&&自从二十世纪五十年代第一台计算机诞生于美国宾夕法尼亚大学,至今不足七十年,这期间人类的计算机科学技术发展之迅速,让人惊叹!这七十年,现代人类中的勇敢的科学探索者们站在巨人们的肩膀上,把前人们的哲学,方法论,数学,物理学等等学科和知识武装成身上的盔甲和手中的长剑,勇敢地冲破层层阻隔,带领着我们整个人类,奔向新的文明制高点!
&&而在统筹计算机系统的硬件资源与软件资源的“英雄”,就是操作系统。
&&很多朋友肯定都有这种感受,“自己苦苦思考出了一个能够实现的精彩构想”,和“去实现这个构想”,完全是两码事!一个大楼的建造之前,必要条件是一份无懈可击的蓝图规划以及工程进展,但是一个才华横溢的土木工程师,虽然深谙土木工程之道,在没有资金的情况下,他无能为力,真要去责怪谁,那只能怪造化弄人。但是,对于计算机科学中的大勇大智者,如果他有了一个准确无误的构想,常人会认为他有两种实现的方式,一是自己从头至尾Coding&and&Coding直到计划完成或者中途夭折,二是用强大的资金来支撑整个构想的步步实现(Bill&Gates先生就属此类)但是还有一种方法!
利用开源!
在对整个构想(操作系统亦可是其中一例)的步步实现都贯彻于心且确认无误的情况之下,在对开源库中的资源有一定的了解的基础知识之上,完全可以以自己精彩绝伦的构想和他人无私的代码奉献,在极短的人年之内,搭建出自己的大系统!(最后一个MakeFile,全部轻松搞定!)。
---uote&from&The&Mythical&Man-Month
向老前辈致敬!
&&5.结束语:
&&“海内存知己,天涯若比邻”,虽然你我不识,但是,我们却在共同进步!
非常荣幸您能花费那么长的时间读完本文,其中一定有不足之处,若不吝赐教,不胜感激!
“天行健&君子以自强不息”
共有20个评论
<span class="a_vote_num" id="a_vote_num_
精神令人赞赏
不过,实现POC和实现可用的东西是两回事
火箭弹和火箭的原理其实是一样的
<span class="a_vote_num" id="a_vote_num_
做应用层的撸过~
精神可嘉,唯独那个汇编码啊,相信没几个会看下来的
<span class="a_vote_num" id="a_vote_num_
实模式下写个简单的“操作系统”没什么难的,调用BIOS中断就可以实现屏幕输出键盘输入磁盘读写了
<span class="a_vote_num" id="a_vote_num_
书架上那本《自己动手写操作系统》看完了就扔那了,虽然明白原理,但连敲个代码去试试的心情都没有。佩服楼主的精神!
<span class="a_vote_num" id="a_vote_num_
OS这种东西,我的意见,要么就针对任务,内核里面加点东西,别改原有的,利用一些内核态的特权,这个属于特殊化操作,也很少用。另一种还是从应用OS本身角度开发,由此需要理解OS的基本原理,写一个最小的OS,确实对个人的认识会有很大帮助。不过貌似在单片机上,连MMU都扔掉,去实现,可能更好点。386其实已经是个很大的系统了。上面做开发,蛮蛋疼的。看linux不如看ucos。这个已经够小了。也不是,人人都能自己写的。有了一定基础,再扩展也不迟。
<span class="a_vote_num" id="a_vote_num_
读读理论就行了。&真要做起来才发现,&操作系统跟硬件打交道。
硬件的复杂度真的是千差万别。&虽然有C这个秘书,汇编这个苦工。
可要想下对正确的命令真不容易。各种硬件只接受特定的命令。
相对于操作系统这个半自动机器人。那些硬件真的算是石器时代的工具。
--- 共有 1 条评论 ---
哈。是啊。当然OS中间还是有不少好东西可以另你发挥想像的。但也总逃不掉硬件的影子。如同说C就套不掉计算机组成原理一样。
(4年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
看到汇编地段果断看评论!
<span class="a_vote_num" id="a_vote_num_
Reply To mallon:阁下果然厉害,一下子就切中关键所在,阁下说的就能解释了早期DOS为什么那么容易崩溃——因为他的CPU-8086没有保护的思想!现代操作系统要比早期DOS要复杂,是因为保护模式!Protected Mode。能淋漓尽致地发挥386保护思想的操作系统,会更加稳定!(intel的那些芯片架构师们确实让人佩服!)
<span class="a_vote_num" id="a_vote_num_
回复 中山野鬼:谢谢你!你让我产生了另一个激动人心的想法!(u/cos很棒,更棒的是开源!)
<span class="a_vote_num" id="a_vote_num_
引用来自“木人mr”的答案读读理论就行了。&真要做起来才发现,&操作系统跟硬件打交道。
硬件的复杂度真的是千差万别。&虽然有C这个秘书,汇编这个苦工。
可要想下对正确的命令真不容易。各种硬件只接受特定的命令。
相对于操作系统这个半自动机器人。那些硬件真的算是石器时代的工具。
很对啊!从Maxwell四大方程组,到PN结的VA特性,到晶体管,到门电路,到小,中,大规模集成电路,到微型控制器和cpu,在到操作系统!操作系统控制整个庞大电子系统的稳定运行,所以,研究操作系统,不是让人兴奋吗!哈哈!
更多开发者职位上
有什么技术问题吗?
Sen_Han...的其它问题
类似的话题要用C语言编写一个简单的代码,可以举一个简单的例子吗,再能具体分析下。_百度知道
要用C语言编写一个简单的代码,可以举一个简单的例子吗,再能具体分析下。
提问者采纳
你的程序入口{主函数;&#47..h文件void main()
&#47,向变量s上逐次累加i+1的值
printf(&;结束循环后输出字符串&quot...+100=%d&#92;
/i&lt,开两个整型变量i和s,s);在i循环中用累加的方式进行计算;/
/ /n&quot,s变量初始值为0
for ( i=0.;100;i++ )
&#47.,1;&#47.+100的累加和】#include&lt,s=0.;1+2+:0,99
s+=(i+1);设置循环100次;
&#47,;由于程序中使用printf函数所以必须引用&#47..h&gt,每次循环i的取值分别是.【计算s=1+2+;stdio.+100=&变量定义;/&#47.,2;1+2+
提问者评价
来自团队:
其他类似问题
6人觉得有用
为您推荐:
其他1条回答
h头文件,io输出很多函数原型在这int main() &#47!&#92.h&);主函数main{
printf(&printf是打印函数;/函数执行完毕后返回0;/ /&#47,j包含n是换行符return
0;//n&quot.代码#include &&#47!
&#92;stdio,在屏幕上打印hello经典的hello world
c语言的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁}

我要回帖

更多关于 c语言代码书写规范 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信