跳到主要內容

發表文章

目前顯示的是 2013的文章

[筆記] Head First Design Patterns

Chapter 1 Welcome to Design Patterns: an introduction OO Basics: Abstraction Encapsulation Polymorphism Inheritance OO Principles: Encapsulate what varies. Favor composition over inheritence. Program to interfaces, not implementations. OO Patterns: Strategy - defined a family of algorithms, encapsulates each once, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it. Chapter 2 Keeping your Objects in the know: the Observer Pattern OO Principles: Strive for loosely coupled designs between objects that interact. OO Patterns: Observer - defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Chapter 3 Decorating Objects: the Decorator Pattern OO Principles: Classes should be open for extension but closed for modification. OO Patterns: Decorator - Attach additional responsibilities to an objec...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...

[轉貼]MySQL 交易功能 Transaction 整理 (XYZ的筆記本)

全文轉貼自: http://xyz.cinc.biz/2013/05/mysql-transaction.html   (XYZ的筆記本) ---------------------------------------------------------------------------------------------------------- 資料庫的交易(Transaction)功能,能確保多個 SQL 指令,全部執行成功,或全部不執行,不會因為一些意外狀況,而只執行一部份指令,造成資料異常。 MySQL 常用的兩個資料表類型:MyISAM、InnoDB, MyISAM  不支援交易功能,所以以下的整理,均是針對 InnoDB  而言。 交易功能4個特性 (ACID)  Atomicity (原子性、不可分割):交易內的 SQL 指令,不管在任何情況,都只能是全部執行完成,或全部不執行。若是發生無法全部執行完成的狀況,則會回滾(rollback)到完全沒執行時的狀態。 Consistency (一致性):交易完成後,必須維持資料的完整性。所有資料必須符合預設的驗證規則、外鍵限制...等。 Isolation (隔離性):多個交易可以獨立、同時執行,不會互相干擾。這一點跟後面會提到的「隔離層級」有關。 Durability (持久性):交易完成後,異動結果須完整的保留。 開始進入交易模式 SQL 指令: START TRANSACTION  或  BEGIN 結束交易模式 交易完成:使用  COMMIT  儲存所有變動,並結束交易。 交易過程異常:使用  ROLLBACK  回滾,取消交易,還原到未進行交易的狀態。(若交易過程連線中斷,沒 COMMIT 提交的變更,亦會如同執行 ROLLBACK 取消交易) 儲存點 (SAVEPOINT) 交易過程中,可標示多個不同的儲存點,有需要時可 ROLLBACK 到某個儲存點。 建立儲存點: SAVEPOINT 名稱 刪除儲存點: RELEASE SAVEPOINT 名稱 ROLLBACK 到某個儲存點: ROLLBACK TO SAVEPOINT 名稱 如...

尾聲-總結,一顆全新的硬碟開始

步驟 使用 fdisk 等工具查看硬碟的分區情況,確定要裝到哪個分區,記下分區的首磁區磁區號 $ bximage -q -hd -mode=flat -size=120 120m.img $ fdisk 120m.img 磁碟 120m.img: 125 MB,125411328 位元組 255 磁頭,63 磁區/磁軌,15 磁柱,總計 244944 磁區 單位 = 磁區 之於 1 * 512 = 512 位元組 磁區大小 (邏輯/實體):512 位元組 / 512 位元組 I/O 大小 (最小/最佳化):512 位元組 / 512 位元組 磁碟識別碼:0x00000000

尾聲-真正的尾聲

grub 系統已經在硬碟上順利執行了,我們仍然可以改進,讓它支持多重開機,採用現有的 grub 即可。 首先,先將開機磁區裝到 Orange'S 分區的開機磁區: dd if=boot/hdboot.bin of=$(HD) seek=`echo "obase=10;ibase=16;\`egrep -e '^ROOT_BASE' boot/include/load.inc | sed -e 's/.*0x//g'\`*200" | bc` bs=1 count=446 conv=notrunc dd if=boot/hdboot.bin of=$(HD) seek=`echo "obase=10;ibase=16;\`egrep -e '^ROOT_BASE' boot/include/load.inc | sed -e 's/.*0x//g'\`*200+1FE" | bc` skip=510 bs=1 count=2 conv=notrunc 並重新編譯(此時,硬碟 80m.img 的開機磁區還是 Orange'S 的開機磁區,可是在 80m.img5 的開機磁區也有 Orange'S 的開機磁區)。

關於Linux系統下Grub啟動流程的討論總結

關於Linux系統下Grub啟動流程的討論總結 轉貼來源: http://www.test104.com/tw/tech/603.html  全世界linuxer都知道grub是什麼東西,但對於MBR引導到grub再引導到具體作業系統的這個流程可能有不少朋友就比較迷糊了。這不,cu上一位朋友就發出了這樣一個求助貼:    假如現在一台電腦上裝了WIN2000系統,那麼我現在在裝上LINUX系統和GRUB,那麼假如把GRUB裝在主分區的話,GRUB直接引導 LINUX和WIN2000,我是可以理解的,因為MBR中是GRUB的STAGE1(對不對呢?),MBR通過檢查DPT分區資訊引導系統跳轉至DBR (活動分區),我這裏想問的活動分區是什麼時候設的呢?那麼裝GRUB到MBR裏,那原來MBR中的WIN的引導資訊是怎麼處理的呢?是不是我們假如說裝 GRUB到MBR的時候,GRUB就把GRUB所在那個區設置為了活動分區了呢?然後GRUB引導時候,MBR就找到那個活動分區找到所需要的檔,然後繼續呢?假如說把GRUB裝到其他分區(非主引導區)的話,那是怎麼樣實現GRUB先啟動的呢?不是先MBR嗎?因為裝到了其他分區,沒有改主引導區,因此主引導區還是WIN2000的引導資料啊,怎麼會GRUB先啟動了呢?這是為什麼呢?跟活動分區有關係沒有呢?我看資料上寫的是哪個系統啟動哪個系統就是活動分區,可是那樣的話,似乎就解釋不通了啊,就是最最開始這個地方一直不懂,理不清楚。

尾聲-從硬碟開機

我們要將 Orange'S 安裝到硬碟上,並實現硬碟啟動。 回憶軟碟啟動的過程: BIOS 將開機磁區讀入記憶體 0000:7C00 處 跳轉到 0000:7C00 處開始執行開機程式碼 開機程式碼從軟碟中找到 loader.bin,並將其讀入記憶體 跳轉到 loader.bin 開始執行 loader.bin 從軟碟中找到 kernel.bin,並將其讀入記憶體 跳轉到 kernel.bin 開始執行,到此可認為啟動過程結束 系統執行中 第 1 步中,係由 CMOS 來決定。第 3 步和第 5 步中, 軟碟啟動:程式碼將在軟碟中尋找 loader.bin 和 kernel.bin 硬碟啟動:需要讓開機磁區程式碼從硬碟中尋找 loader.bin 並讓 loader 從硬碟中尋找 kernel.bin 故我們必須重寫 boot.asm 和 loader.asm,讓它們讀取硬碟而不是軟碟。新的檔我們取名為 hdboot.asm 和 hdldr.asm。

尾聲-一些瑣碎的事兒

問題一:我們的系統每次啟動都是「全新」的,上次建立的文件到下一次啟動就不見了。 問題二:每次都會執行一次解開 cmd.tar 的操作。 問題三:再一次解壓縮時,可能已有包含的檔案了

記憶體管理-簡單的 shell

有了 fork() 和 exec(),我們可以實現簡單的 shell 了。目前只實現一種功能:讀取命令並執行之(如果命令存在的話)。

記憶體管理-exec

exec 係用以將現在的處理序映射替換成另一個。亦即我們可從硬碟讀取另一個可執行檔,用它替換掉剛剛被 fork 出來的子處理序。 為測試 exec,勢必先有一個簡單的可執行檔(echo)。因此,我們將製作一個類似於 C 的 Run-time:把過去可供給應用程式使用的函數單獨連結成一個檔,以後直接連結即可。 目前包括: 兩個真正的系統使用 sendrec 和 printx lib/syscall.asm 字串操作 memcopy、memset、strcpy、strlen lib/string.asm FS 的介面 lib/open.c lib/read.c lib/write.c lib/close.c lib/unlink.c MM 的介面 lib/fork.c lib/exit.c lib/wait.c SYS 的介面 lib/getpid.c 其他 lib/misc.c lib/vsprintf.c lib/printf.c 將以上檔案單獨連結成一個檔:oragescrt.a ar rcs lib/orangescrt.a lib/syscall.asm lib/string.asm \ lib/open.c lib/read.c lib/write.c lib/close.c lib/unlink.c \ lib/fork.c lib/exit.c lib/wait.c \ lib/getpid.c \ lib/misc.c lib/vsprintf.c lib/printf.c 並寫一個最簡單的 echo(與 pwd)。 最後,想「安裝」一些應用程式到我們的檔案系統中,有如下工作: 編寫應用程式,並編譯連結 將連結好的應用程式打成一個 tar 包:inst.tar 將 inst.tar 用工具 dd 寫入磁片(映射)的某段特定磁區(假設這一段的第一個扇區的磁區號為 X) 啟動系統,這時 mkfs() 會在檔案系統中建立一個新檔 cmd.tar,它的 inode 中的 i_start_sect 成員會被設為 X 在某個處理序中-比如 Init-將 cmd.tar 解包,將其中包含的檔存入文件系統

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

記憶體管理-exit 和 wait

假設處理序 P 有子處理序 A。而 A 使用 exit(),那麼 MM 將會: 告訴 FS:A 退出,請作相應處理 釋放 A 佔用的記憶體 判斷 P 是否正在 WAITING 如果是 清除 P 的 WAITING 位 向 P 發送訊息以解除阻塞(到此 P 的 wait() 函數結束) 釋放 A 的處理序表項(到此 A 的 exit() 函數結束) 如果否 設定 A 的 HANGING 位 瀏覽 proc_table[],如果發現 A 有子處理序 B,那麼 將 Init 處理序設定為 B 的父處理序(換言之,將 B 過繼給 Init) 判斷是否滿足 Init 正在 WAITING 且 B 正在 HANGING 如果是 清除 Init 的 WAITING 位 向 Init 發送訊息以解除阻塞(到此 Init 的 wait() 函數結束) 釋放 B 的處理序表項(到此 B 的 exit() 函數結束) 如果否 如果 Init 正在 WAITING 但 B 並沒有 HANGING,那麼「握手」會在將來 B 使用 exit() 時發生 如果 B 正在 HANGING 但 Init 並沒有 WAITING,那麼「握手」會在將來 Init 使用 wait() 時發生 如果 P 使用 wait(),那麼 MM 將會: 瀏覽 proc_table[],如果發現 A 是 P 的子處理序,並且它正在 HANGING,那麼 向 P 發送訊息以解除阻塞(到此 P 的 wait() 函數結束) 釋放 A 的處理序表項(到此 A 的 exit() 函數結束) 如果 P 的子處理序沒有一個再 HANGING,則 設 P 的 WAITING 位 如果 P 壓根兒沒有子處理序,則 向 P 發送訊息,訊息攜帶一個表示出錯的返回值(到此 P 的 wait() 函數結束)

記憶體管理-fork

一個新的處理序需要: 自己的程式碼、資料、堆疊 在 proc_table[] 中佔用一個位置 在 GDT 中佔用一個位置,用以存放處理序對應的 LDT 描述符號 問題在於,第一項的程式碼、資料、堆疊從何而來?傳統上,生成一個新處理序時,係直接由某個已執行的處理序處來繼承或複製。 生成子處理序的系統使用及稱之為 fork()。

檔案系統-將 TTY 納入檔案系統

寫入 TTY 跟寫入普通檔是很類似的,不同之處在於 TTY 不需要進行任何埠操作,只是寫入顯示卡記憶體即可。而對於讀出操作,則有很大的不同。TTY 收到處理序讀出請求的訊息,並不能馬上返回資料,因為此時並沒有任何輸入,而需要使用者輸入字元,等輸入結束後,TTY才可以返回給處理序。問題在於: 怎樣是「輸入結束」:是美一次鍵盤敲擊後算結束(面向字元,Raw mode)?還是等敲 Enter 才算結果(面向行,Cooked mode)?或者其它? 是否要讓檔案系統等待輸入過程結束? TTY 需要馬上向檔案系統發送訊息以示返回。而檔案系統則完全應該阻塞想要得到輸入的處理序,直到輸入結束。 讀 TTY 的過程: 假設處理序 P 要求讀取 TTY,它會發送訊息給檔案系統 檔案系統將訊息傳遞給 TTY TTY 記下發出請求的處理序號等資訊後立即返回 檔案系統此時並不對 P 接觸阻塞,因為結果尚未準備好 檔案系統一如往常等待任何處理序之請求 TTY 將鍵盤輸入複製進 P 傳入的記憶體位址 一旦遇到 Enter,TTY 就告訴檔案系統,P 的請求已被滿足 檔案系統會解除對 P 的阻塞 讀取工作結束 寫 TTY 的過程: P 發訊息給檔案系統 檔案系統傳遞給 TTY TTY 受到訊息候立即將字元寫入顯示卡記憶體(保持 P 和 FS 處理序的阻塞) 完成後發訊息給檔案系統 檔案系統在發訊息給 P 整個過程結束

檔案系統-建立檔案、與其它相關函數

file descriptor 一個檔案在檔案系統中涉及五個要素: 檔案內容(資料)所佔用的磁區 i-node i-node 在 inode-map 中佔用的一位 資料磁區在 sector-map 中佔用的一位或多位 檔案在目錄中佔有的目錄項(direntry) 建立檔案 為檔案內容(資料)分配磁區 在 inode_array 中分配一個 i-node 在 inode-map 中分配一位 在 sector-map 中分配一位或多位 在相應目錄中寫入一個目錄項(direntry) 刪除檔案 釋放 inode-map 中的相應位 釋放 sector-map 中的相應位 刪除根目錄中的目錄項

檔案系統-完善硬碟驅動並製作一個檔案系統

前篇只是完成 DEV_OPEN,必須完成後續的 DEV_READ 和 DEV_WRITE,以利檔案的操作。 「檔案系統」需要: 有地方存放 Metadata-super block 有地方紀錄磁區的使用情況-sector map 有地方來紀錄任一檔的資訊,比如佔用哪些磁區等-i-node map、inode_array (存放真正的 i-node) 有地方存放檔的索引-root 資料區 檔案系統

檔案系統-硬碟驅動程式

請先建立一顆硬碟(80MB),分為 1 個 primary partition, 1 個 extended partitions, 4 個 logical partitions。 而我在此處採用 VirtualBox,故需要將 raw file 轉成 vdi 格式。 $ bximage -hd -mode=flat -size=80 -q 80m.img $ fdisk 80m.img $ VBoxManage convertfromraw -format VDI 80m.img 80m.vdi 硬碟分割結果(自由切割)

處理序間通訊(IPC, Inter-Process Communication)

採用微核心。 IPC 分為兩種: 非同步 IPC:送完訊息就不管了 同步 IPC:發送者一直等到接收者收到訊息才放手,接收者接不到訊息就一直等著。優勢如下: 作業系統無須另外維護緩衝區來存放正在傳遞的訊息 作業系統無須保留一份訊息副本 作業系統無須維護接收佇列(發送佇列還是需要的) 發送者和接收者都可在任何時刻清晰且容易第知道訊息是否送達。 假設有處理序 A 想要向 B 發送訊息 M,其過程如下: A 首先準備好 M A 透過系統使用 sendrec,最終使用 msg_send 簡單判斷是否發生鎖死 判斷目標處理序 B 是否正在等待來自 A 的訊息 如果是:訊息被複製給 B,B被解除阻塞,繼續執行 如果否:A 被阻塞,並被加入到 B 的發送佇列中 假設有處理序 B 想要接收訊息(來自特定處理序、中斷或者任意處理序),其過程如下: B 準備一個空的訊息結構體 M,用於接收訊息 B 通過系統使用 sendrec,最終使用 msg_receive 判斷 B 是否有個來自硬體的訊息(通過 has_int_msg),如果是,且 B 準備接收來自中斷的訊息或準備接收任意訊息,則馬上準備一個訊息給 B,並返回。 如果 B 想接收來自任意處理序的訊息,則從自己的發送佇列中選取締一個(如果佇列非空的話),將其訊息複製給 M 如果 B 想接收來自特定處理序 A 的訊息,則先判斷 A 是否正在等待向 B 發送訊息,若是的話,將其訊息複製給 M 如果此時沒有任何處理序發訊息給 B,B 會被阻塞

輸入/輸出系統-printf

printf 使用過程示意圖 程式碼 include/const.h (修改) #define NR_SYS_CALL 2 include/global.h (新增) extern TASK user_proc_table[]; include/proc.h (修改) typedef struct s_proc { STACK_FRAME regs; /* process registers saved in stack frame */ u16 ldt_sel; /* gdt selector giving ldt base and limit */ DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */ int ticks; /* remained ticks */ int priority; u32 pid; /* process id passed in from MM */ char p_name[16]; /* name of the process */ int nr_tty; /* 用以指定此處理序的 TTY */ } PROCESS; /* Number of tasks & procs */ #define NR_TASKS 1 #define NR_PROCS 3 include/string.h (新增) PUBLIC int strlen(char* p_str); include/type.h (新增) typedef char * va_list; kernel/proc.c (修改) for (p = proc_table; p < proc_table+NR_TASKS+NR_PROCS; p++) { for(p=proc_table;p < proc_table+NR_TASKS+...

輸入/輸出系統-TTY

TTY任務 在 TTY 任務中執行一個迴圈,此迴圈將輪詢每一個 TTY,處理它的事件,包括從鍵盤緩衝區讀取資料、顯示字元等內容。 並非每輪詢到某個 TTY 時,箭頭所對應的全部事件都會發生,只有當某個 TTY 對應的控制台是現在控制台時,它才可以讀取鍵盤緩衝區(虛線) TTY 可以對輸入的資料作更多的處理,但在這裡,我們只把它簡化為「顯示」一項。 雖然圖中鍵盤和顯示器的畫在 TTY 的外面,但應把鍵盤和顯示器算做每一個 TTY 的一部分,它們是公用的。 執行的過程如上,則輪詢到每個 TTY 不外乎作兩件事: 處理輸入:查看是不是現在 TTY,如果是則從鍵盤緩衝區讀取資料 處理輸出:如果有要顯示的內容則要顯示它 TTY 任務程式碼示意 與先前程式實現的區別在於: 每個 TTY 都應該有自己的讀和寫的動作。所以在 keyboard_read() 內部,函數需要了解自己是被哪一個 TTY 使用。我們透過為函數傳入一個參數來作到這一點,此參數指向當前 TTY 的指針。 為了讓輸入和輸出分離,被 keyboard_read() 使用的 in_process() 不應該在直接回顯字元,而應該將回顯的任務交給 TTY 來完成,這樣,我們就需要為每個 TTY 建立一塊緩衝區,用以放置將被回顯的字元。 每個 TTY 回顯字元時操作的 CONSOLE 是不同的,故每個 TTY 都應該有一個成員來記載其對應的 CONSOLE 資訊。

輸入/輸出系統-鍵盤

敲擊鍵盤有兩個方面的含義:動作和內容。 動作可分為三類:按下、保持按住的狀態、放開 內容則是鍵盤上不同的鍵。 敲擊鍵盤所產生的編碼被稱作掃描碼(Scan Code),它分為 Make Code 和 Break Code 兩類。 當一個鍵被按下或者保持住按下時,將會產生 Make Code,當鍵盤彈起時,產生 Break Code。 除了 Pause 鍵之外,每個按鍵都對應一個 Make Code 和一個 Break Code。 當 8048 檢測到一個鍵的動作後,會把相應的掃描碼發送給 8042,8042會把它轉換成相應的 Scan code set1 掃描碼,並將其放置在輸入緩衝區中,然後 8042 告訴 8259A 產生中斷(IRQ1)。如果此時鍵盤又有新的鍵被按下,8042 將不再接收,一直到緩衝區被清空,8042 才會收到更多的掃描碼。

遲到的處理序-處理序調度

程式碼 include/proc.h /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ proc.h ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Forrest Yu, 2005 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ typedef struct s_stackframe { /* proc_ptr points here ↑ Low */ u32 gs; /* ┓ │ */ u32 fs; /* ┃ │ */ u32 es; /* ┃ │ */ u32 ds; /* ┃ │ */ u32 edi; /* ┃ │ */ u32 esi; /* ┣ pushed by save() │ */ u32 ebp; /* ┃ │ */ u32 kernel_esp; /* <- 'popad' will ignore it │ */ u32 ebx; /* ┃ ↑棧從高地址往低地址增長*/ u32 edx; /* ┃ │ */ u32 ecx; /* ┃ │ */ u32 eax; /* ┛ │ */ u32 retaddr; /* return address for assembly code save() │ */ u32 eip; /* ┓ │ */ u32 cs; /* ┃ │ */ u32 eflags; /* ┣ these are pushed by CPU during interrupt │ */ u32 ...