どうもみむらです。
いきなり質問ですが、下記の2つの文章の共通点をあげてください。
A.里の秋。
静かな静かな 里の秋
お背戸に木の実の 落ちる夜は
ああ 母さんとただ二人
栗の実 煮てます いろりばた
B.静かな湖畔
静かな湖畔の 森のかげから
もう起きちゃいかがと カッコが鳴く
カッコ カッコ
カッコ カッコ カッコ
はい、もうわかりましたよね。
こたえは、誰しもがわかるあれです。
人って時々、文章に起こしたい時があるじゃないですか。
どうもみむらです。
いきなり質問ですが、下記の2つの文章の共通点をあげてください。
A.里の秋。
静かな静かな 里の秋
お背戸に木の実の 落ちる夜は
ああ 母さんとただ二人
栗の実 煮てます いろりばた
B.静かな湖畔
静かな湖畔の 森のかげから
もう起きちゃいかがと カッコが鳴く
カッコ カッコ
カッコ カッコ カッコ
はい、もうわかりましたよね。
こたえは、誰しもがわかるあれです。
なんだろう、何百番煎じな気がする。もうお茶も出なくなってお湯ですよ、お湯。
フォロワーの @pasberth さんが、「JIT ってどういう仕掛けになってるの!」といっていたので、
メモリ上にバイトコードを置いて実行する方法ということでちょろっと書いたものです。
とりあえず、ピタゴラスの定理をアセンブラで書いて、
C でぺけぺけ。
ひとまず、私の作業環境である Windows での動作確認。
そいでもって、x86 なら、どのプラットフォームでもバイナリ部分は変えなくても走るよ! ってのを示すために、
Mac を立ち上げて実行。
#include <mach/mach.h>
#include <stdio.h>
/************************************************
とりあえず、中のデータは16進で記録してあって、
リトルエンディアンですから、
0x01020304 は、メモリ上に 0x04,0x03,0x02,0x01 の順で格納されます。
要は逆順です。
また、命令のオーダーについては、必ずしも何バイトということはありませんが、
最長命令は 32bit アーキテクチャですんで 4 バイト ( 32 bit / 8 = 4 byte. )
見やすくするために、2バイトや1バイト命令に関しては、
0x90 (NOP) [NOP = なにもしない] を挿入して、アライメント(区切り)を合わせてあります。
まー、適当に見てもらえれば。
************************************************/
int main(){
unsigned long d;
int a = 0,b = 0,c = 0;
unsigned long code[32] = {0};
printf("vm_protect : %s\n\n",vm_protect(
mach_task_self(),
(vm_address_t)code,32 * sizeof(long),
FALSE,
VM_PROT_READ |
VM_PROT_WRITE |
VM_PROT_EXECUTE
) == KERN_SUCCESS ? "[OK]" : "[FALSE]");
printf("INPUT [a,b,c] : ");
scanf("%d,%d,%d",&a,&b,&c);
code[0] = 0x0424448B; // MOV EAX,DWORD PTR SS:[ESP+4]
code[1] = 0x08244C8B; // MOV ECX,DWORD PTR SS:[ESP+8]
code[2] = 0x9090C13B; // CMP EAX,ECX
code[3] = 0x9090047E; // JLE SHORT ; EIP+4
code[4] = 0x9090C88B; // MOV ECX,EAX
code[5] = 0x0C24548B; // MOV EDX,DWORD PTR SS:[ESP+C]
code[6] = 0x9090CA3B; // CMP ECX,EDX
code[7] = 0x9090147E; // JLE SHORT ; EIP+C
code[8] = 0x90909056; // PUSH ESI
code[9] = 0x9090F28B; // MOV ESI,EDX
code[10] = 0x9090D18B; // MOV EDX,ECX
code[11] = 0x9090CE8B; // MOV ECX,ESI
code[12] = 0x9090905E; // POP ESI
code[13] = 0x90C0AF0F; // IMUL EAX,EAX
code[14] = 0x90C9AF0F; // IMUL ECX,ECX
code[15] = 0x90D2AF0F; // IMUL EDX,EDX
code[16] = 0x9090C103; // ADD EAX,ECX
code[17] = 0x9090C22B; // SUB EAX,EDX
code[18] = 0x9090D8F7; // NEG EAX
code[19] = 0x9090C01B; // SBB EAX,EAX
code[20] = 0x90909040; // INC EAX
code[21] = 0x909090C3; // RETN
printf("\nRETURN : %s\n",
((int(*)(int,int,int))code)(a,b,c) == 0 ? "[FALSE]" : "[OK]");
return 0;
}
中のバイトコード部分は、ソースコード中のコメントを見てもらえれば。
本来、こういうページ領域上にバイトコードを置いて、それを呼び出そうとしても、
実行できないように設定されています。
(実行できるようになっていると、プロセスに脆弱性があったとき、悪意のあるコードを攻撃者から送信されて、実行されてしまう危険性が格段に上がる)
ですが、さすがに完全に実行できないと、Java VM をはじめとしたソフトウェアが稼働できなくなってしまうため、
OSから実行可能にフラグを書き換えるAPIが提供されています。
OS | 関数 |
| Windows | VirtualProtect MSDN : http://msdn.microsoft.com/ja-jp/library/cc430214.aspx |
| Mac OS X | vm_protect The GNU Mach Reference Manual : http://www.gnu.org/software/hurd/gnumach-doc/Memory-Attributes.html Darwin : http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/vm_protect.html |
| Linux | mprotect Man page of MPROTECT : http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/mprotect.2.html |
実行可能なバイトコードは、x86 プロセッサ共通なので、同じプロセッサならどれでも動きますが、
各オペレーティングシステムに合わせて、上記に挙げた関数を利用して、実行可能に設定する必要があります。
実際の JIT コンパイラなどでは、各プラットフォームごとのライブラリや、
APIの差異の吸収が必要になって、結構面倒なことになりますが、
今回の例はきわめて単純な例を使って、 x86 プロセッサ上なら同じコードが動くということを示すのと同時に、
Java などでは、こういう感じでたぶんコードを実行しているということで。
(mono はこれ使ってる感濃厚。 ソースコードを眺めていた感じでは。
ども。Mimura です。
答辞の文章つくったり、ICTスクールの成果物作ってみたり、
二秒ぐらい逆出校メンバーに混じって掃除手伝ってみたり、
何かと忙しい日々ですが、息抜きということで、新しいソフト作ってます。
本当はペンの選択の部分を画像使ってやってみたり、ツールバーセットしてみたり、
元に戻すとかの履歴機能を使えるようにしたり、それ以前にレイヤーを搭載してみたりいろいろとやりたいのですが、まだ出来ていないのが実情です。はい。すいません。
今のところ、内部構造としてはレイヤーやキャンバスの概念が搭載されていて、
複数の画像を同時編集したり、大量のレイヤーを使用したりも出来ます。
出来る用にはなっているのですが、インターフェイスの製作が圧倒的に間に合ってません。どうしよう。
筆圧の入力に対応して、ペンタブレット接続環境では筆圧が反映されます。
そして、このソフト、絵の周りの色選択ツールやペン選択ツールなどのそれぞれのウィンドウが、
それぞれ独立して動くような仕掛けになっていて、
キャンバスやレイヤーが格納されているメイン部分(カーネル)には内部定義してあるメッセージを飛ばしてやりとりする構造となっています。
また、頻繁にアクセスするオブジェクトに関してはエクスポートされている関数を呼び出すことでアクセスできるようになっています。
そしてそして、ペンについても同様の構造になっていて、それぞれが独立した物体になってます。
なんでそんな構造にしたのかというと、将来的にプラグイン方式を付けたいと思ったため。
そして、絵がへたっぴで、授業の「美術」と聞いただけで身の毛がよだつほどの私ですから、
なるべくそういう部分を、慣れている人に作ってもらえたらいいなぁ。なんて思っていたり思わなかったり。
・・。でもそういう人って大抵、自分でソフト書いちゃいますよね。むぅ。
ある程度プログラムが固まったら、SDK付きで公開してみようかなぁ。なんてことを考えています。
心優しい方がいましたら、プラグイン作って、すばらしいペンやツールを作ってもらえると有難いです。
プラグインの製作はあんちゅことないですし、デフォルトのツールもあんちゅことないです。
まぁー。そんな感じで。
とりあえず、現時点では描画時に CPU を 100% 50%取られるので、 (手持ちの Eee PC 901-X にて)
(製作用マシン [C2Q Q6600] では 22%ほど持って行く。)
最適化しないとなぁ。と考えています。 DIB で自分で数式演算書いた方が早いのかなぁ。
それとも DDB で BitBlt とかガリガリやった方がいいのかなぁ。 むぅ。
助言お待ちしています。
02/10編集。
あ。何寝ぼけてたんだろう。 Eee-PC 901-X は デュアルコアですから、100% 取ることはないです。
(シングルタスクのプログラムのため)
それでも50% 近くは取っていきます。 シングルコアの100%みたいなところですね。
隣で眠ってるシングルコアのマシンで動かすと、他のプログラムの処理分が多少ありますが、
合計値としては100%になります。 何れにせよ、構造を見直さないとまずいですよね。
覚え書き。(突っ込み入ったので、語弊がないように一部書き直し。
弱いんだかなんだか、評価は出来ませんが、
とりあえず、CUDA コンパイラ(nvcc) はマクロ展開に際して非常によろしくない挙動を取ります。
CUDAコンパイラは糞。マクロを正常に展開しない。 – 簡潔で覚えやすいタイトルを3秒で思いつく程度の能力
詳細については上記リンクの記事を参照して頂ければとおもいます。
とりあえず、
HWND hwnd = CreateWindow(lpClassName, // CLASS NAME
lpWindowName, //WINDOWNAME
というような、引数が複数行にわたって記述される場合で、コメントを1行コメントなんぞ付けたりすると、
マクロが良い感じで展開してくれないよ-。ということ。
C コンパイラというから、きっと、全部コメントは /* */ だろ!ということで、
NVIDIAの中の人がかなり手を抜いているような気がしてなりません。
/* ~ */ で括れば、とりあえず、コメントの範囲はわかるし! みたいな。うん。
なんかそんなこと考えたら余程ステキな仕様な気がしてきた。うん。
ま・・まぁ、とりあえず、
ユーザはコメントを /* */ で書いて、
NVIDIAの中の人にはマクロ展開部分をきっちり作ってもらって。と。
でも、C99 って1行コメント使えるよなぁ・・。
Cでも使える時代が到来しているのに・・。むぅ。
うーん。MFC には相当の裏がありそうだなぁ・・。 ・・。ども Mimura です。
STEP_M のフォントサイズ問題で悩んでいたのですが、
ソースコードは一緒なのに、なんでコンパイルした結果が違うのさ。と。
MFCは奥が深いです。Microsoft のパンドラの箱。のような気もしないでもないですが、どうなんでしょうか。
少なくとも、SDKプログラミングばっかりやってた自分には、ちょっと謎です。
Vista の動作結果ではほぼ変わらないのですが、
XPで動かすとすばらしく変わります。なんで。と思うほど。
(同じソースコードですが、頭に WIN_VER 指定をかけてあります。)
現在公開中のもの (WIN_VER = 0×600 (Windows Vista))
XP 宣言したもの(WIN_VER = 0×501 (Windows XP or 2003))
なんでこー。ツールチップのサイズがここまで変わってくるのさー!!
・・そんなわけで、最初の修正の時にはツールチップのフォント取得部分がおかしいんだろう。と思って、フォント設定をシステムフォントから取得するようにしましたが、
まさか、本当の答えは WIN_VER だったとは。
・・。挙動が変わられても困ります。本当に。
MFC怖いなぁ。と思った今日この頃です。
(Microsoft さん。教えてもらえるとありがたいです。このあたりについて・・。 あ。あれですか。 .NET つかえ! っていうことですか。多分そうですか。)
最近のコメント