功能:程序讀取每條記錄,並顯示每條記錄中的「名」linux
記錄讀取功能主要執行如下步驟:函數
一、打開文件oop
二、讀取一條記錄spa
三、若到文件末尾,則退出;不然計算「名」的字符數指針
四、將「名」寫入STDOUT中code
五、輸出換行符到STDOUT中內存
六、返回,並讀取另外一條記錄ci
因爲每條記錄中,「名」的長度不定,所以須要一個函數來統計寫入的字符數。該程序存於count-chars.s字符串
#目的:對記錄中字符數進行統計,遇到空字符結束 #輸入:字符串地址 #輸出:計數值返回到%eax中 #變量: %ecx——字符計數 # %al——當前字符 # %edx——當前字符地址 .type count_chars, @function .globl count_chars .equ ST_STRING_START_ADDRESS, 8 #字符串開始地址 count_chars: pushl %ebp movl %esp, %ebp movl $0, %ecx #計數器從0開始 movl ST_STRING_START_ADDRESS(%ebp), %edx #數據的起始地址 count_loop_begin: movb (%edx), %al #獲取當前字符,採用間接尋址方式 cmpb $0, %al #判斷是否爲空字符 je count_loop_end incl %ecx incl %edx jmp count_loop_begin count_loop_end: movl %ecx, %eax #結束循環,將統計的字數存入%eax中 popl %ebp ret
該程序存於文件write-newline.s文件中input
.include "linux.s" .globl write_newline .type write_newline, @function .section .data newline: .ascii "\n" .section .text .equ ST_FILEDES, 8 write_newline: pushl %ebp movl %esp, %ebp movl $SYS_WRITE, %eax movl ST_FILEDES(%ebp), %ebx movl $newline, %ecx movl $1, %edx int $LINUX_SYSCALL movl %ebp, %esp popl %ebp ret
該程序存放在文件read-records.s中。
.include "linux.s" .include "record-def.s" .section .data file_name: .ascii "test.dat\0" .section .bss .lcomm record_buffer, RECORD_SIZE .section .text .globl _start #主程序 _start: #定義存儲輸入輸出描述符的棧位置 #也可用一個.data段中的內存地址代替 .equ ST_INPUT_DESCIPTOR, -4 .equ ST_OUTPUT_DESCRIPTOR, -8 movl %esp, %ebp subl $8, %esp #打開文件 movl $SYS_OPEN, %eax movl $file_name, %ebx movl $0, %ecx #表示只讀打開 movl $0666, %edx int $LINUX_SYSCALL movl %eax, ST_INPUT_DESCRIPTOR(%ebp) #保存輸入文件描述符 movl $STDOUT, ST_OUTPUT_DESCRIPTOR(%ebp) #保存輸出文件描述符,此處爲「標準輸出」 record_loop: pushl ST_INPUT_DESCRIPTOR(%ebp) #輸入文件描述符 pushl $record_buffer #緩衝區地址指針 call read_record addl $8, %esp #比較讀取的字節數和緩衝區大小 #若是不一致,說明達到文件尾部或出錯 cmpl $RECORD_SIZE, %eax jne finished_reading #打印名,需先知道「名」的大小 pushl $RECORD_FIRSTNAME + record_buffer #該指令將兩個常數相加,獲得的結果爲內存地址 #獲得的結果將入棧。RECORD_FIRSTNAME記錄從 #起始地址到名字字段之間的字節數 call count_chars addl $4, %esp movl %eax, %edx movl ST_OUTPUT_DESCRIPTOR(%ebp), %ebx movl $SYS_WRITE, %eax movl $RECORD_FIRSTNAME + record_buffer, %ecx int $LINUX_SYSCALL jmp recor_loop finish_reading: movl $SYS_EXIT, %eax movl $0, %ebx int $LINUX_SYSCALL
編譯、連接、執行上面程序
as read-record.s -o read-record.o
as count-chars.x -o count-chars.o
as write-newline.s -o write-newline.o
as read-records.s -o read-records.o
ld read-record.o count-chars.o write-newline.o read-records.o -o read-records
修改程序主要有如下步驟:
一、打開輸入文件和輸出文件
二、從輸入文件讀取記錄
三、遞增年齡
四、新紀錄寫入文件
將下面程序存入文件add-year.s中
.include "linux.s" .include "record-def.s" .section .data input_file_name: .ascii "test.dat\0" output_file_name: .ascii "testout.dat\0" .section .bss .lcomm record_buffer, RECORD_SIZE #局部變量的棧偏移量 .equ ST_INPUT_DESCRIPTOR, -4 .equ ST_OUTPUT_DESCRIPTOR, -8 .section .text .globl _start _start: movl %esp, %ebp subl $8, %esp #打開輸入文件 movl $SYS_OPEN, %eax movl $input_file_name, %ebx movl $0, %ecx movl $0666, %edx int $LINUX_SYSCALL movl %eax, ST_INPUT_DESCRIPTOR(%ebp) #輸入文件的文件描述符 #打開輸出文件 movl %SYS_OPEN, %eax movl $output_file_name, %ebx movl $0101, %ecx movl $0666, %edx int $LINUX_SYSCALL movl %eax,ST_OUTPUT_DESCRIPTOR(%ebp) loop_begin: pushl ST_INPUT_DESCRIPTOR(%ebp) pushl $record_buffer call read_record addl $8, %esp #判斷是否到達文件尾部,或出現讀錯誤 cmpl $RECORD_SIZE, %eax jne loop_end #遞增年齡 incl record_buffer + RECORD_AGE #注意此處爲直接尋址,和上面的「pushl $a+b」形式區別 #寫記錄 pushl ST_OUTPUT_DESCRIPTOR(%ebp) pushl $record_buffer call write_record addl $8, %esp jmp loop_begin loop_end: movl $SYS_EXIT, %eax movl $0, %ebx int $LINUX_SYSCALL
編譯、連接、運行上面程序
as add-year.s -o add-years.o
ld add-year.o read-record.o write-record.o -o add-years
./add-years
在上面add-years.s程序的基礎上,咱們添加下面這個程序。該程序打印錯誤消息並退出,其做爲「被調函數」,由add-year.s程序調用。該函數存於文件:error-exit.s。爲了使用該函數,需將錯誤信息的地址和錯誤代碼入棧,而後進行調用。
.include "linux.s" .equ ST_ERROR_CODE, 8 .equ ST_ERROR-MSG, 12 .globl .error_exit .type error_exit, @function error_exit: pushl %ebp movl %esp, %ebp #寫錯誤代碼 movl ST_ERROR_CODE(%ebp), %ecx pushl %ecx call count_chars #計算字符數 popl %ecx movl %eax, %edx #字符數量 movl $STDERR, %ebx movl $SYS_WRITE, %eax int $LINUX_SYSCALL #寫錯誤信息 movl STD_ERROR_MSG(%ebp), %ecx pushl %ecx call count_chars popl %ecx movl %eax, %edx movl $SYS_WRITE, %eax movl $STDERR, %ebx int $LINUX_SYSCALL pushl $STDERR call write_newlines #退出 movl $SYS_EXIT, %eax movl $1, %ebx int $LINUX_SYSCALL
以上爲處理出錯時的函數。下面將給出add-years.s是如何使用該函數的。
前面的add-year.s在打開文件後並未進行檢錯——可能出現找不到文件等狀況。因此下面程序對打開文件後的返回碼進行檢測,保證正確打開文件。
#打開供讀取的文件 movl $SYS_OPEN, %eax movl $input_file_name, $ebx movl $0, %ecx movl $0666, %edx int $LINUX_SYSCALL movl %eax, INPUT_DESCRIPTOR(%ebp) #將返回碼存入棧中,供檢測 #如下將對%eax存儲的返回碼進行檢測,若爲負值,則調用錯誤函數 cmpl $0, %eax jl continue_processing #0<%eax #發送錯誤消息 .section .data no_open_file_code: .ascii "0001:\0" no_open_file_msg: .ascii "cant open input file\0" .section .text pushl $no_open_file_msg pushl $no_open_file_code call error_exit continue_processing: #程序其他部分
編譯、連接、運行程序:
as add-year.s -o add-year.o
as error-exit.s -o error-exit.o
ld add-year.o write-newline.o error-exit.o read-record.o write-record.o count-chars.o -o add-year