2013年12月28日 星期六

提供自己的 operator new/delete

the 1st edition: 20131228
the 2nd edition: 20160219

這不是在說明 overloaded operator new, 這件事情所有的 c++ 入門書籍都會告知你, 若你看的那本沒有, 可以考慮換掉它。

本範例一樣是作業系統等級的程式碼, 在完成了 global/static object 的研究之後, 來看看怎麼實作 new/delete, 在 c++ 若不能使用 new/delete, 好像說不過去。

測試方式:
在 linux compile 成 dos .com 檔, 然後放到 qemu/dos 上執行。

no_impl_new_delete.cpp
 1 __asm__(".code16gcc\n");
 2 #include "io.h"
 3 #include "obj.h"
 4 
 5 typedef signed char s8;
 6 typedef signed short s16;
 7 typedef signed int s32;
 8 
 9 typedef unsigned char u8;
10 typedef unsigned short u16;
11 typedef unsigned int u32;
12 
13 #define BOCHS_MB __asm__ __volatile__("xchg %bx, %bx");
14 
15 extern int _start_ctors;
16 extern int _end_ctors;
17 
18 void s16_print_int(int i, int radix);
19 void print_str(const char   *s);
20 
21 extern int obj_count;
22 
23 
24 const int SOME_BASE_ADDRESS = 0x500;
25 
26 void myfree(void *ptr)
27 {
28   print_str("myfree\r\n");
29 }
30 
31 void *mymalloc(int size)
32 {
33   print_str("size: ");
34   s16_print_int(size, 10);
35   print_str("\r\n");
36   static char mem[256];
37   return mem;
38 }
39 

52 
53 extern "C" int cpp_main(void)
54 {
55   print_str("cpp_main\r\n");
56   s16_print_int(obj_count, 10);
57   print_str("\r\n");
58   
59   Io *io_p = new Io;
60   const char *ver=__VERSION__;
61   io_p->print("io_p g++ version: ");
62   io_p->print(ver);
63   io_p->print("\r\n");
64   delete io_p;
65 
66   return 0;
67 }

L59 和我們平常使用的 c++ new 用法一模一樣, new 會做類似 malloc 的動作, 先去要一塊記憶體, 並呼叫 ctor, 可是在作業系統之前的程式, 根本沒有記憶體管理的程式碼 (記得我們沒有 c++ runtime 來幫我們做這件事情嗎?), 來看看編譯會怎麼樣?

descent@debian-vm:dos_cpp$ make new_test.bin
ld -m elf_i386 -static -Tcpp.ld -nostdlib -M -o new_test.elf cpp_init.o new_test.o io.o cpp_abi.o obj.o dos_io.o > new_test.elf.map
new_test.o: In function `cpp_main':
/home/descent/git/simple_os/cpp_runtime/global_object/dos_cpp/new_test.cpp:59: undefined reference to `operator new(unsigned int)'
/home/descent/git/simple_os/cpp_runtime/global_object/dos_cpp/new_test.cpp:64: undefined reference to `operator delete(void*)'
make: *** [new_test.elf] Error 1

呵呵, 果然有預期的錯誤, 要是沒錯誤反而令我害怕。訊息很容易理解, 沒定義 operator new, operator delete, 所以該怎麼辦? 不難, 自己定義就好了。

new_test.cpp L41 ~ L49 就提供了 operator new, operator delete, 比想像中還要簡單吧! 這時候再編譯就沒問題了。有問題的是那個 mymalloc 要怎麼寫是吧?

這個的確是個問題, 得先搞定記憶體管理才能把這部份寫好, 一開始也困擾我許久, 因為我這部份還沒做得很好 (有趣吧! 我的 simple os 竟然沒有記憶體管理機制), 不過我只想搞定 c++ runtime new/delete, 並不是要實作記憶體管理機制 (這個就不容易了), 所以想了個簡單的辦法來模擬 malloc 的行為。

L26, L31 的 mymalloc/myfree 應該很簡單, 簡單到不用說明吧?

new_test.cpp
 1 __asm__(".code16gcc\n");
 2 #include "io.h"
 3 #include "obj.h"
 4 
 5 typedef signed char s8;
 6 typedef signed short s16;
 7 typedef signed int s32;
 8 
 9 typedef unsigned char u8;
10 typedef unsigned short u16;
11 typedef unsigned int u32;
12 
13 #define BOCHS_MB __asm__ __volatile__("xchg %bx, %bx");
14 
15 extern int _start_ctors;
16 extern int _end_ctors;
17 
18 void s16_print_int(int i, int radix);
19 void print_str(const char   *s);
20 
21 extern int obj_count;
22 
23 
24 const int SOME_BASE_ADDRESS = 0x500;
25 
26 void myfree(void *ptr)
27 {
28   print_str("myfree\r\n");
29 }
30 
31 void *mymalloc(int size)
32 {
33   print_str("size: ");
34   s16_print_int(size, 10);
35   print_str("\r\n");
36   static char mem[256];
37   return mem;
38 }
39 
40 // ref: http://wiki.osdev.org/C%2B%2B#The_Operators_.27new.27_and_.27delete.27
41 void *operator new(unsigned int s)
42 {
43   return mymalloc(s);
44 }
45 
46 void operator delete(void *p)
47 {
48   myfree(p);
49 }
50 
51 extern "C" int cpp_main(void)
52 {
53   print_str("cpp_main\r\n");
54   s16_print_int(obj_count, 10);
55   print_str("\r\n");
56   
57   Io *io_p = new Io;
58   const char *ver=__VERSION__;
59   io_p->print("io_p g++ version: ");
60   io_p->print(ver);
61   io_p->print("\r\n");
62   delete io_p;
63 
64   return 0;
65 }

L57 new 發動時, L41 operator new 會被 c++ 編譯器傳入正確的 class size, 在 Io class 這個例子, size 是 4, fig 1便是在 dos 下的執行結果。可以看到除了呼叫配置記憶體的程式碼之外, new 還會「順便」呼叫 constructor, 這才是 c++ 的 new 和 malloc 不一樣的地方, c++ compiler 會知道要喚起那些 class 的 constructor, 很神奇吧! c++ compiler 難寫是有道理的。

你寫了一行的 new, 但 c++ compiler 卻產生兩行 code, c++ 的黑箱作業可不是說假的。

fig 1

比起 global/static object, operator new/delete 簡單到令我驚訝 (當然, 真的要處理記憶體的分配與回收就沒那麼簡單了)。所以現在你知道在作業系統之下寫 c++ 程式, c++ runtime library 的辛勞了。

source code:
https://github.com/descent/simple_os
commit 93e327234d153636e315721ca66f73df0e7c0887
cpp_runtime branch

這是 g++ 裡頭的實作, 不難懂。


gcc-4.9.2/libstdc++-v3/libsupc++/new_op.cc

 1 // Support routines for the -*- C++ -*- dynamic memory management.
 2 
 3 // Copyright (C) 1997-2014 Free Software Foundation, Inc.
 4 //
 5 // This file is part of GCC.
 6 //
 7 // GCC is free software; you can redistribute it and/or modify
 8 // it under the terms of the GNU General Public License as published by
 9 // the Free Software Foundation; either version 3, or (at your option)
10 // any later version.
11 //
12 // GCC is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // Under Section 7 of GPL version 3, you are granted additional
18 // permissions described in the GCC Runtime Library Exception, version
19 // 3.1, as published by the Free Software Foundation.
20 
21 // You should have received a copy of the GNU General Public License and
22 // a copy of the GCC Runtime Library Exception along with this program;
23 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24 // <http://www.gnu.org/licenses/>.
25 
26 #include <bits/c++config.h>
27 #include <cstdlib>
28 #include <bits/exception_defines.h>
29 #include "new"
30 
31 using std::new_handler;
32 using std::bad_alloc;
33 #if _GLIBCXX_HOSTED
34 using std::malloc;
35 #else
36 // A freestanding C runtime may not provide "malloc" -- but there is no
37 // other reasonable way to implement "operator new".
38 extern "C" void *malloc (std::size_t);
39 #endif
40 
41 _GLIBCXX_WEAK_DEFINITION void *
42 operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
43 {
44   void *p;
45 
46   /* malloc (0) is unpredictable; avoid it.  */
47   if (sz == 0)
48     sz = 1;
49   p = (void *) malloc (sz);
50   while (p == 0)
51     {
52       new_handler handler = std::get_new_handler ();
53       if (! handler)
54  _GLIBCXX_THROW_OR_ABORT(bad_alloc());
55       handler ();
56       p = (void *) malloc (sz);
57     }
58 
59   return p;
60 }

ref:
The Operators 'new' and 'delete'

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。