1. カーネル空間およびユーザランドのソフトウェアの開発技術 |
---|
|
システムコールによるカーネルとユーザランドの連携 | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
カーネルとユーザプロセスの仮想メモリ空間 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
オペレーティングシステムの基本構造 |
---|
┌─────────────────────────┐ │┌───────────────────────┐│ ││ ユーザランドプログラム ││ │└───────────────────────┘│ │ ↑ ┌───────────┐│ ユーザプロセス空間 │ │ │ ライブラリ ││ │ │ └───────────┘│ └───────│───────────↑─────┘ │ │ ┌───────↓───────────↓─────┐ │┌───────────────────────┐│ ││ システムコールインタフェース ││ │└───────────────────────┘│ │┌─────────┐ ┌───────────┐│ ││ プロセス管理 │ │ ファイルシステム ││ │└─────────┘ │ [バッファキャッシュ] ││ │ └───────────┘│ │┌─────────┐ ┌───────────┐│ カーネル空間 ││ メモリ管理 │ │ ネットワーク層 ││ │└─────────┘ └───────────┘│ │ │ │┌─────────┐ ┌───────────┐│ ││ アーキテクチャ │ │ デバイスドライバ ││ ││ 依存コード │ │ ││ │└─────────┘ └───────────┘│ └──────↑────────────↑─────┘ │ │ ↓ ↓ ┌─────────────────────────┐ │ ハードウェア │ └─────────────────────────┘ |
プログラム開発の視点からの比較 | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
カーネルソースコードの構成 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
※上記のステップ数は6.2-RELEASE時点のものであるが、最近のリリースでも大幅な増加は無い
|
デバイスドライバ |
---|
ハードウェアデバイスの存在を検出(probe)し、デバイス自身およびソフトウェア状態を初期化する。 システムコールインタフェース経由の入出力要求を処理し、必要に応じてカレントプロセスを中断させる(tsleep()を使用)。 システムが割り込みを受け取った時に呼び出される割り込みハンドラであり、中断させたプロセスを再開させる(wakeup()を使用)。 入力要求 出力要求 │ │ │ ┌──────────────────┐ │ │ │┌──────┐ ┌──────┐│ │ │ ││入力バッファ│ │出力バッファ││←┘ │ │└──────┘ └──────┘│ Top Half └→│─┐ ↑ │ │ -----┼--┼------┼------------------┼----┼---- │ │ ┌────────┐ │ │ │ │ │割り込みハンドラ│ │ │ Bottom Half │ │ └────────┘ │ │ │ │ ↑ ↑ │ │ └─┼───┼─────┼───┼──┘ ↓ │ │ ↓ ┌────────────────┐ │ ハードウェアデバイス │ └────────────────┘ |
カーネルのデバッグ |
---|
どこまで実行が進んだのか、あるいはどこで変数の内容が破壊されたのかを調べる原始的な方法。
突然リブートして出力内容を確認できない場合、調べたい個所に無限ループやhlt命令を埋め込む。
机上デバッグの段階でC言語ソースのレベルでは気づきにくいバグも、アセンブリ言語ソースの レベルで調べると判明するバグも多い。 カーネルのビルドにおいて、"cc -c"となっているところを"cc -S"に変えてアセンブリ言語ソースを生成させる。 カーネルに内蔵されているアセンブリ言語レベルの簡易デバッガとしてFreeBSDにはDDBがある。 カーネル内部処理での例外発生時にリブートする前に起動される。また、コンソールから「Ctrl+PrtSc(またはCtrl+Alt+Esc)」を 入力することで、手動で起動可能。 FreeBSDではシステムクラッシュ時に実メモリの内容を専用パーティションへ書き込み、リブート後に その内容をクラッシュダンプとしてファイルへ格納し、gdbによるソースレベルの解析をおこなうことが可能。 シリアルケーブル(NULLモデムケーブル)で接続されたPC側でgdbを起動してオンラインカーネルデバッグをおこなうことも可能。 |
カーネル組み込みデバッガ: DDB | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
ソースレベルカーネルデバッガ: KGDB |
---|
[root@freebsd ~]# cd /sys/i386/compile/VMBSD [root@freebsd /sys/i386/compile/VMBSD]# kgdb kernel.debug /var/crash/vmcore.0 [snip] panic: free: address 0xcd26abac(0xcd26a000) has not been allocated. cpuid = 0 KDB: enter: panic Uptime: 17h13m27s Physical memory: 243 MB Dumping 58 MB: 43 27 11 #0 doadump () at pcpu.h:246 246 __asm __volatile("movl %%fs:0,%0" : "=r" (td)); (kgdb) bt #0 doadump () at pcpu.h:246 #1 0xc08aa777 in boot (howto=260) at ../../../kern/kern_shutdown.c:416 #2 0xc08aaa12 in panic (fmt=Variable "fmt" is not available. ) at ../../../kern/kern_shutdown.c:590 #3 0xc089716d in free (addr=0x0, mtp=0xc0d99140) at ../../../kern/kern_malloc.c:444 #4 0xc074ce78 in seq_write (dev=0xc2597900, uio=0xcd26ac58, flag=0) at ../../../dev/seq/seq.c:97 #5 0xc082b41f in devfs_write_f (fp=0xc2b5b3b8, uio=0xcd26ac58, cred=0xc2a30c00, flags=0, td=0xc2d27a00) at ../../../fs/devfs/devfs_vnops.c:1509 #6 0xc08e97f7 in dofilewrite (td=0xc2d27a00, fd=1, fp=0xc2b5b3b8, auio=0xcd26ac58, offset=-1, flags=0) at file.h:239 #7 0xc08e9ae8 in kern_writev (td=0xc2d27a00, fd=1, auio=0xcd26ac58) at ../../../kern/sys_generic.c:446 #8 0xc08e9b6f in write (td=0xc2d27a00, uap=0xcd26acf8) at ../../../kern/sys_generic.c:362 #9 0xc0bed483 in syscall (frame=0xcd26ad38) at ../../../i386/i386/trap.c:1111 #10 0xc0bcf180 in Xint0x80_syscall () at ../../../i386/i386/exception.s:261 #11 0x00000033 in ?? () Previous frame inner to this frame (corrupt stack?) (kgdb) up #1 0xc08aa777 in boot (howto=260) at ../../../kern/kern_shutdown.c:416 416 doadump(); (kgdb) up #2 0xc08aaa12 in panic (fmt=Variable "fmt" is not available. ) at ../../../kern/kern_shutdown.c:590 590 boot(bootopt); (kgdb) up #3 0xc089716d in free (addr=0x0, mtp=0xc0d99140) at ../../../kern/kern_malloc.c:444 444 panic("free: address %p(%p) has not been allocated.\n", (kgdb) up #4 0xc074ce78 in seq_write (dev=0xc2597900, uio=0xcd26ac58, flag=0) at ../../../dev/seq/seq.c:97 97 free(&seq_buf, M_TEMP); (kgdb) up #5 0xc082b41f in devfs_write_f (fp=0xc2b5b3b8, uio=0xcd26ac58, cred=0xc2a30c00, flags=0, td=0xc2d27a00) at ../../../fs/devfs/devfs_vnops.c:1509 1509 error = dsw->d_write(dev, uio, ioflag); (kgdb) down #4 0xc074ce78 in seq_write (dev=0xc2597900, uio=0xcd26ac58, flag=0) at ../../../dev/seq/seq.c:97 97 free(&seq_buf, M_TEMP); (kgdb) list 92 sequence_number = val; 93 sx_xunlock(&sequence_lock); 94 break; 95 } 96 97 free(&seq_buf, M_TEMP); 98 99 return (error); 100 } 101 (kgdb) p sequence_lock $1 = {lock_object = {lo_name = 0xc0ca0122 "sequence", lo_flags = 36896768, lo_data = 0, lo_witness = 0x0}, sx_lock = 1} (kgdb) quit |
[演習] 擬似デバイスドライバ seq の開発とデバッグ |
---|
$ head -1 /dev/seq 0 $ head -1 /dev/seq 1 $ cat /dev/seq 2 3 4 (中略) 123456 ^C $ |
[演習] 擬似デバイスドライバ seq の開発とデバッグ(続き) |
---|
34 static struct sx sequence_lock; 35 static unsigned long sequence_number = 0; 36 static char sequence_buffer[DEV_BSIZE]; [snip] 47 /* ARGSUSED */ 48 static int 49 seq_read(struct cdev *dev __unused, struct uio *uio, int flag) 50 { 51 int c = 0, error; 52 void *seq_buf; 53 54 sx_xlock(&sequence_lock); 55 snprintf(sequence_buffer, sizeof(sequence_buffer), 56 "%lu\n", sequence_number); 57 seq_buf = (void *)sequence_buffer; 58 59 c = strlen(sequence_buffer); 60 error = uiomove(seq_buf, c, uio); 61 if (error == 0) 62 { 63 sequence_number++; 64 } 65 66 sx_xunlock(&sequence_lock); 67 return (error); 68 } # cd /sys/dev/seq # vi seq.c # cd /sys/i386/compile/VMBSD # make # make install # shutdown -r now |
[演習] 擬似デバイスドライバ seq の開発とデバッグ(続き) |
---|
現在、シーケンス番号を生成する関数 seq_read() は問題なく動作しているが、シーケンス番号を設定する関数 seq_write() はシステムクラッシュ(panic)を引き起こす。 カーネル組み込みデバッガDDBおよびカーネルデバッガKGDBを使用して原因を調査し、デバイスドライバのソースコードを適切に修正せよ。 シーケンス番号は32ビット長となっているが、これを64ビット長に拡張したい。 この要望に合うようにデバイスドライバのソースコードを適切に修正せよ。 デバイスドライバ seq のソースコードを参考に、POSIX Epoch (1970年1月1日0:00(GMT))からの経過秒数を示す文字列を 取得するデバイスドライバ time を作成せよ。 カーネル内部の現在時刻の取得にはシステムコールインタフェース /sys/kern/kern_time.c:gettimeofday()、 経過秒数への変換はライブラリtime(3)のソース /usr/src/lib/libc/gen/time.c を参考にする。 なお、新規のデバイスドライバのソースコードの組み込みには、/sys/conf/files、/sys/i386/conf/VMBSDにある seq 用の記述を参考にする。 新規デバイスを追加したので、カーネルの再構築とリビルドを以下の手順でおこなう必要がある。
# rm -rf /sys/i386/compile/VMBSD # cd /sys/i386/config # vi VMBSD ←device timeを追加 # config VMBSD # cd ../compile/VMBSD # make cleandepend; make depend # make # make install # shutdown -r now また、以下のようにカーネルモジュールとしてコンパイルし動的にロードすることで、 リブートすることなくデバイスドライバtimeのテストが可能。
# cd /sys/modules/time # vi /sys/dev/time/time.c # make # make install # kldload time # cat /dev/time # kldunload time |
2. RDBMSとJavaによるWebアプリケーションの開発 |
---|
Webの機能を動的に拡張するために考案されたサーバサイド技術。プロセス生成・消滅のオーバヘッドや状態保持の問題。 Microsoftによって開発された技術で、HTMLにVBScriptを埋め込んだページをWebサーバのモジュールであるスクリプトエンジンによって実行する。 MS IISが前提環境。 クライアントサイドの技術で、SunMicrosystemsによって開発されたJava言語で記述されたアプリケーションを、 Webブラウザ上にダウンロードして実行させる技術。 生産性、マルチメディア(特に音声や動画)の扱いが劣ることから、競合技術であるFlashにシェアを奪われる。 クライアントサイドの技術で、Netscape 社が開発したスクリプト言語で、Webブラウザ側にあるインタプリタで実行される。 Java言語とは互換性がまったく無い。Ajaxの基盤技術の一つになっている。 JavaクラスをWebサーバへロードして、メソッドを呼び出すことで動的な応答をさせる技術。 Javaプログラムのメソッド内でHTMLを生成するため、ページのデザインが難しい。 ASPに対抗して開発された技術で、ASPと同様にHTMLにJavaコードを埋め込む。Servletの弱点を克服。 ロジックとデザインの分離を可能とし、さまざまなプラットフォームで稼動できる。 ASPやJSPで実現されたロジックとデザインの分離を、より明確にブロック分けしたMicrosoftによって開発された技術。 開発言語としてC#、VBなど複数のものが選択可能となり、どの言語でも同一の.NET Frameworkのライブラリが使用できる。 サーバとの非同期通信というアプローチを採ったJavaScriptを主軸とした複合技術。 ページ遷移を行わずにコンテンツの動的更新が可能となる。 |
Webアプリケーション実行環境の典型的構成 |
---|
┌────────┐ ┌───┐ ┌───┐ ┌───┐ │ Webクライアント│←──→│ │ │ │ │ │ └────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌────────┐ │ Web │ │ AP │ │ DB │ │ Webクライアント│←──→│サーバ│←→│サーバ│←→│サーバ│ └────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌────────┐ │ │ │ │ │ │ │ Webクライアント│←──→│ │ │ │ │ │ └────────┘ └───┘ └───┘ └───┘ WebクライアントからのHTTPによるアクセス要求を分散処理する。 HTTPトランザクションの一貫性を保持し、システム固有の処理をおこない、バックエンドで動作するデータベースなどの検索/加工処理などを司る。 今回の演習ではTomcatを使用する。 システムのデータや管理情報を司る。今回の演習ではPostgreSQLを使用する。 |
[演習]会議室予約システムの仕様 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Enter your user name and mail address (User ID = 2) List of rooms
|
[演習]会議室予約システムの仕様(続き) |
---|
(User ID = 2) Available rooms (User ID = 2) Confirmation |
[演習]会議室予約システムの構築準備: DB作成 |
---|
1 DROP SEQUENCE user_master_uids_seq; 2 3 CREATE SEQUENCE user_master_uids_seq 4 START WITH 1 5 INCREMENT BY 1 6 NO MAXVALUE 7 NO MINVALUE 8 CACHE 1; 9 10 DROP TABLE user_master; 11 12 CREATE TABLE user_master ( 13 uid INT 14 DEFAULT NEXTVAL('user_master_uids_seq'::text) NOT NULL, 15 user_name VARCHAR(128) 16 NOT NULL, 17 mail_address VARCHAR(128) 18 NOT NULL, 19 20 CONSTRAINT user_master_pk PRIMARY KEY ( 21 uid 22 ) 23 ); 24 25 INSERT INTO user_master(user_name, mail_address) 26 VALUES('iwasaki', 'iwasaki@freebsd.org'); 27 28 DROP TABLE room_master; 29 30 CREATE TABLE room_master ( 31 rid INT, 32 room_name VARCHAR(128), 33 capacity INT, 34 35 CONSTRAINT room_master_pk PRIMARY KEY ( 36 rid 37 ) 38 ); 39 40 INSERT INTO room_master(rid, room_name, capacity) VALUES(1, 'room#01', 10); 41 INSERT INTO room_master(rid, room_name, capacity) VALUES(2, 'room#02', 10); 42 INSERT INTO room_master(rid, room_name, capacity) VALUES(3, 'room#03', 20); 43 INSERT INTO room_master(rid, room_name, capacity) VALUES(4, 'room#04', 20); 44 INSERT INTO room_master(rid, room_name, capacity) VALUES(5, 'room#05', 30); 45 46 DROP TABLE room_reservation; 47 48 CREATE TABLE room_reservation ( 49 rid INT, 50 reserved_from TIMESTAMP, 51 reserved_to TIMESTAMP, 52 uid INT, 53 54 CONSTRAINT room_reservation_pk PRIMARY KEY ( 55 rid, 56 reserved_from, 57 reserved_to 58 ) 59 ); 60 61 INSERT INTO room_reservation(rid, reserved_from, reserved_to, uid) 62 VALUES(1, TO_TIMESTAMP('2012-11-01 08', 'yyyy-mm-dd hh24'), 63 TO_TIMESTAMP('2012-11-01 12', 'yyyy-mm-dd hh24'), 1); 64 INSERT INTO room_reservation(rid, reserved_from, reserved_to, uid) 65 VALUES(2, TO_TIMESTAMP('2012-11-01 07', 'yyyy-mm-dd hh24'), 66 TO_TIMESTAMP('2012-11-03 20', 'yyyy-mm-dd hh24'), 1); |
[演習]会議室予約システムの構築準備: DB作成(続き) |
---|
$ psql < create_db.sql DROP SEQUENCE CREATE SEQUENCE DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "user_master_pk" for table "user_master" CREATE TABLE INSERT 0 1 DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "room_master_pk" for table "room_master" CREATE TABLE INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "room_reservation_pk" for table "room_reservation" CREATE TABLE INSERT 0 1 INSERT 0 1 DROP FUNCTION CREATE FUNCTION is_occupied ------------- f (1 row) [snip] is_occupied ------------- f (1 row) $ |
[演習]会議室予約システムの構築準備: DB作成(続き2) |
---|
$ psql psql (8.4.4) Type "help" for help. iwasaki=> \? General \copyright show PostgreSQL usage and distribution terms \g [FILE] or ; execute query (and send results to file or |pipe) \h [NAME] help on syntax of SQL commands, * for all commands \q quit psql Query Buffer \e [FILE] edit the query buffer (or file) with external editor \ef [FUNCNAME] edit function definition with external editor \p show the contents of the query buffer \r reset (clear) the query buffer \s [FILE] display history or save it to file \w FILE write query buffer to file Input/Output \copy ... perform SQL COPY with data stream to the client host \echo [STRING] write string to standard output \i FILE execute commands from file \o [FILE] send all query results to file or |pipe \qecho [STRING] write string to query output stream (see \o) Informational (options: S = show system objects, + = additional detail) \d[S+] list tables, views, and sequences \d[S+] NAME describe table, view, sequence, or index \da[+] [PATTERN] list aggregates \db[+] [PATTERN] list tablespaces \dc[S] [PATTERN] list conversions \dC [PATTERN] list casts \dd[S] [PATTERN] show comments on objects \dD[S] [PATTERN] list domains \des[+] [PATTERN] list foreign servers \deu[+] [PATTERN] list user mappings \dew[+] [PATTERN] list foreign-data wrappers \df[antw][S+] [PATRN] list [only agg/normal/trigger/window] functions \dF[+] [PATTERN] list text search configurations \dFd[+] [PATTERN] list text search dictionaries \dFp[+] [PATTERN] list text search parsers \dFt[+] [PATTERN] list text search templates \dg[+] [PATTERN] list roles (groups) \di[S+] [PATTERN] list indexes \dl list large objects, same as \lo_list \dn[+] [PATTERN] list schemas \do[S] [PATTERN] list operators \dp [PATTERN] list table, view, and sequence access privileges \ds[S+] [PATTERN] list sequences \dt[S+] [PATTERN] list tables \dT[S+] [PATTERN] list data types \du[+] [PATTERN] list roles (users) \dv[S+] [PATTERN] list views \l[+] list all databases \dF[+] [PATTERN] list text search configurations \dFd[+] [PATTERN] list text search dictionaries \dFp[+] [PATTERN] list text search parsers \dFt[+] [PATTERN] list text search templates \dg[+] [PATTERN] list roles (groups) \di[S+] [PATTERN] list indexes \dl list large objects, same as \lo_list \dn[+] [PATTERN] list schemas \do[S] [PATTERN] list operators \dp [PATTERN] list table, view, and sequence access privileges \ds[S+] [PATTERN] list sequences \dt[S+] [PATTERN] list tables \dT[S+] [PATTERN] list data types \du[+] [PATTERN] list roles (users) \dv[S+] [PATTERN] list views \l[+] list all databases \z [PATTERN] same as \dp Formatting \a toggle between unaligned and aligned output mode \C [STRING] set table title, or unset if none \f [STRING] show or set field separator for unaligned query output \H toggle HTML output mode (currently off) \pset NAME [VALUE] set table output option (NAME := {format|border|expanded|fieldsep|footer|null| numericlocale|recordsep|tuples_only|title|tableattr| pager}) \t [on|off] show only rows (currently off) \T [STRING] set HTML <table> tag attributes, or unset if none \x [on|off] toggle expanded output (currently off) Connection \c[onnect] [DBNAME|- USER|- HOST|- PORT|-] connect to new database (currently "iwasaki") \encoding [ENCODING] show or set client encoding \password [USERNAME] securely change the password for a user Operating System \cd [DIR] change the current working directory \timing [on|off] toggle timing of commands (currently off) \! [COMMAND] execute command in shell or start interactive shell Variables \prompt [TEXT] NAME prompt user to set internal variable \set [NAME [VALUE]] set internal variable, or list all if no parameters \unset NAME unset (delete) internal variable Large Objects \lo_export LOBOID FILE \lo_import FILE [COMMENT] \lo_list \lo_unlink LOBOID large object operations iwasaki=> \d List of relations Schema | Name | Type | Owner --------+----------------------+----------+--------- public | room_master | table | iwasaki public | room_reservation | table | iwasaki public | user_master | table | iwasaki public | user_master_uids_seq | sequence | iwasaki (4 rows) -- room_masterのすべてのレコードを取得 iwasaki=> select * from room_master; rid | room_name | capacity -----+-----------+---------- 1 | room#01 | 10 2 | room#02 | 10 3 | room#03 | 20 4 | room#04 | 20 5 | room#05 | 30 (5 rows) -- room_reservationのすべてのレコードを取得 iwasaki=> select * from room_reservation; rid | reserved_from | reserved_to | uid -----+---------------------+---------------------+----- 1 | 2012-11-01 08:00:00 | 2012-11-01 12:00:00 | 1 2 | 2012-11-01 07:00:00 | 2012-11-03 20:00:00 | 1 (2 rows) -- user_masterのすべてのレコードを取得 iwasaki=> select * from user_master; uid | user_name | mail_address -----+-----------+--------------------- 1 | iwasaki | iwasaki@freebsd.org (1 row) -- room_reservationとuser_masterを内部結合してレコードを取得 iwasaki=> select r.*, u.user_name from room_reservation r, user_master u iwasaki-> where r.uid = u.uid; rid | reserved_from | reserved_to | uid | user_name -----+---------------------+---------------------+-----+----------- 1 | 2012-11-01 08:00:00 | 2012-11-01 12:00:00 | 1 | iwasaki 2 | 2012-11-01 07:00:00 | 2012-11-03 20:00:00 | 1 | iwasaki (2 rows) -- room_masterにroom_reservationを外部結合して -- room_masterのすべてのレコードとroom_reservationの対応するレコードを取得 iwasaki=> select m.*, r.uid from room_master m iwasaki-> left outer join iwasaki-> (select * from room_reservation iwasaki-> where ((TO_TIMESTAMP('2012-11-01 07', 'yyyy-mm-dd hh24') iwasaki-> between reserved_from and reserved_to) or iwasaki-> (TO_TIMESTAMP('2012-11-01 09', 'yyyy-mm-dd hh24') iwasaki-> between reserved_from and reserved_to)) iwasaki-> ) r iwasaki-> on m.rid = r.rid; rid | room_name | capacity | uid -----+-----------+----------+----- 1 | room#01 | 10 | 1 2 | room#02 | 10 | 1 3 | room#03 | 20 | 4 | room#04 | 20 | 5 | room#05 | 30 | (5 rows) -- room_reservation更新のトランザクション iwasaki=> begin; BEGIN iwasaki=> update room_reservation set uid = 2 where rid = 1; UPDATE 1 iwasaki=> select * from room_reservation; rid | reserved_from | reserved_to | uid -----+---------------------+---------------------+----- 1 | 2012-11-01 08:00:00 | 2012-11-01 12:00:00 | 2 2 | 2012-11-01 07:00:00 | 2012-11-03 20:00:00 | 1 (2 rows) -- 更新をロールバックしてトランザクション取り消し iwasaki=> rollback; ROLLBACK iwasaki=> select * from room_reservation; rid | reserved_from | reserved_to | uid -----+---------------------+---------------------+----- 1 | 2012-11-01 08:00:00 | 2012-11-01 12:00:00 | 1 2 | 2012-11-01 07:00:00 | 2012-11-03 20:00:00 | 1 (2 rows) iwasaki=> \q $ |
[演習]会議室予約システムの構築準備: Javaクラスのコンパイル |
---|
$ cd course/day-2/roomReservation/WEB-INF/classes/ $ javac -classpath . simpleApp/*.java 1 package simpleApp; 2 3 import java.sql.*; 4 import javax.sql.*; 5 import javax.naming.*; 6 7 public class DataAccess 8 { 9 private DataSource ds_; 10 private Connection con_; 11 private Statement st_; 12 private ResultSet rs_; 13 14 public DataAccess() 15 { 16 ds_ = null; 17 con_ = null; 18 st_ = null; 19 rs_ = null; 20 } 21 22 public synchronized void open() 23 throws Exception 24 { 25 Context ctx = new InitialContext(); 26 ds_ = (DataSource)ctx.lookup("java:/comp/env/jdbc/postgres"); 27 con_ = ds_.getConnection(); 28 st_ = con_.createStatement(); 29 } 30 31 public ResultSet getResultSet(String sql) 32 throws Exception 33 { 34 if (st_.execute(sql)) { 35 rs_ = st_.getResultSet(); 36 return rs_; 37 } 38 return null; 39 } 40 41 public void execute(String sql) 42 throws Exception 43 { 44 st_.execute(sql); 45 } 46 47 public PreparedStatement prepareStatement(String sql) 48 throws Exception 49 { 50 return con_.prepareStatement(sql); 51 } 52 53 public synchronized void close() 54 throws Exception 55 { 56 if (rs_ != null ) { 57 rs_.close(); 58 } 59 if (st_ != null ) { 60 st_.close(); 61 } 62 if (con_ != null ) { 63 con_.close(); 64 } 65 } 66 } 67 |
[演習]会議室予約システムの構築準備: Javaクラスのコンパイル(続き) |
---|
1 package simpleApp; 2 3 import java.sql.*; 4 import simpleApp.*; 5 6 public class UserManager 7 { 8 private DataAccess da_; 9 10 public UserManager() 11 { 12 da_ = new DataAccess(); 13 } 14 15 public int search(String user_name, String mail_address) 16 throws Exception 17 { 18 int uid = -1; 19 20 try { 21 da_.open(); 22 23 String sql = "SELECT uid FROM user_master WHERE " + 24 "user_name=? AND mail_address=?"; 25 PreparedStatement ps = da_.prepareStatement(sql); 26 ps.setString(1, user_name); 27 ps.setString(2, mail_address); 28 ResultSet rs = ps.executeQuery(); 29 if (rs.next()) { 30 uid = rs.getInt("uid"); 31 } 32 rs.close(); 33 ps.close(); 34 } finally { 35 da_.close(); 36 } 37 38 return uid; 39 } 40 41 public int add(String user_name, String mail_address) 42 throws Exception 43 { 44 try { 45 da_.open(); 46 47 String sql = "INSERT INTO user_master" + 48 "(user_name, mail_address) VALUES(?, ?)"; 49 50 PreparedStatement ps = da_.prepareStatement(sql) 51 ps.setString(1, user_name); 52 ps.setString(2, mail_address); 53 ps.executeUpdate(); 54 55 ps.close(); 56 } finally { 57 da_.close(); 58 } 59 60 return search(user_name, mail_address); 61 } 62 } 63 |
[演習]会議室予約システムの構築 | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
会議室の検索結果に、予約希望期間にすでに予約が入っている会議室も含まれているため、
その会議室を予約処理するとダブルブッキングが生じてしまう。
会議室の検索結果の表示の際にすでに予約済みの会議室が選択できないよう、search.jspを修正せよ。
is_occupied(?, TO_TIMESTAMP('2012-11-01 08', 'yyyy-mm-dd hh24'), TO_TIMESTAMP('2012-11-01 10', 'yyyy-mm-dd hh24'))
会議室の検索結果の表示の際にすでに予約済みの会議室があった場合、予約した人と連絡が取れるよう
e-mailアドレスが表示されると便利である。この要望に合うよう、search.jspを修正せよ。
ミッション1でおこなった修正は会議室一覧の情報を取得しながら、会議室ごとに予約済みかを判定して会議室一覧の
情報を加工するものであり、扱う会議室の数が増えれば性能の問題が発生することは容易に想像できる。
この一連のDBへのアクセスは、実は一回のSELECTでおこなうことが可能である。
その方法を検討し、処理を効率的におこなうようsearch.jspを修正せよ。
ログイン後、自分が予約した会議室の予約情報を表示し、取り消し処理がおこなえるようにしたい。 search.jspを修正し、取り消し処理をおこなうcancel.jspを作成せよ。 なお、cancel.jspでは予約取り消し(Cancel Reservations)ボタンとsearch.jspへ戻る(Back)ボタンを用意すること。
複数の会議室を一度に予約する場合、他のユーザの処理と競合してデータの矛盾が発生する可能性がある。
simpleApp.DataAccessクラスを拡張し、トランザクションの機能を追加せよ。
またreserve.jspをトランザクションを使用してデータの矛盾が起こらないように修正せよ。
このシステムではJSPファイルでSQLを記述しDBアクセスをおこなっているが、これはデザインとロジックの分離の観点から好ましくない。 simleApp.UserManagerクラスを参考に、会議室予約業務ロジックを担当するsimpleApp.ReservationManagerクラスを設計および実装し、 これを使用するようすべてのJSPファイルを修正せよ。 このシステムには隠された不具合が多数存在する。それを発見し修正せよ。 |
3. 2時間で覚えるObjective-C |
---|
|
メソッド呼びだしとクラス定義 |
---|
|
Objective-Cの基本事項のまとめ |
---|
|
Objective-Cのサンプルプログラム |
---|
#import <stdio.h> #import <Foundation/Foundation.h> int main(int argc, char *argv[]) { int rv = 0; id pool = [NSAutoreleasePool new]; { if (argc != 2) { NSString *msg = [NSString stringWithFormat: @"usage: %s url_string", argv[0]]; printf("%s\n", [msg UTF8String]); rv = 1; goto out; } NSString *address = [NSString stringWithCString: argv[1]]; NSURL *url = [NSURL URLWithString: address]; NSURLRequest *request = [NSURLRequest requestWithURL: url]; NSURLResponse *response = nil; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error]; if ([[error localizedDescription] length] > 0) { NSLog(@"ConnectionError: %@", [error localizedDescription]); rv = 2; goto out; } [data writeToFile: @"output.dat" atomically: YES]; } out: [pool release]; return rv; } |
サンプルプログラムの解説 |
---|
|
NSURLConnectionの同期通信と非同期通信 |
---|
|
NSURLConnectionの非同期通信のDelegate |
---|
/usr/local/GNUstep/System/Library/Headers/Foundation/ |
NSURLConnectionのDelegate実装例 |
---|
@interface MyConnectDelegate : NSObject { // 更新可能なバイナリデータ格納用オブジェクト NSMutableData *result; } @end @implementation MyConnectDelegate - (void)connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response { NSLog(@"didRecieveResponse: %@", response); // レスポンスを受け取ったらNSMutableDataインスタンス生成 result = [[NSMutableData alloc] init]; } - (void)connection: (NSURLConnection *)connection didReceiveData: (NSData *)data { NSLog(@"didReceiveData"); // データを受け取ったらNSMutableDataインスタンスへ追加 [result appendData: data]; } - (void)connectionDidFinishLoading: (NSURLConnection *)connection { NSLog(@"connectionDidFinishLoading"); // ロードが完了したらファイルへ出力 [result writeToFile: @"output.dat" atomically: YES]; } @end |
NSURLConnectionでPOSTメソッドのリクエストを送信 |
---|
NSString *address = @"http://localhost:8180/roomReservation/login2.jsp"; NSURL *url = [NSURL URLWithString: address]; // POSTデータを作成 NSString *param = @"user_name=iwasaki&mail_address=iwasaki@freebsd.org"; NSData *body = [param dataUsingEncoding: NSUTF8StringEncoding]; // 更新可能なrequest NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: url]; [request setHTTPMethod: @"POST"]; [request setValue: @"application/x-www-form-urlencoded" forHTTPHeaderField: @"content-type"]; [request setHTTPBody: body]; NSURLResponse *response = nil; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error]; |
[演習]Objective-C |
---|
サンプルプログラムで解説したurlget.mは、引数で指定されたURLにアクセスして コンテンツをファイルoutput.datに格納する。 生成されたoutput.datのサイズが正しいかを確認できるよう、レスポンスの ContentLengthをコンソールに出力するよう修正せよ。 $ ./urlget http://localhost:8180/ urlget2.mはDelegateを使用したサンプルプログラムの非同期通信版である。 GUIアプリケーションでは同期通信をおこなうと画面表示処理が止まってしまうため、 非同期通信をおこなうことが多い。 urlget2.mの実行中にダウンロードの進行状況をパーセント表示するよう、プログラムを修正せよ。 $ ./urlget2 http://localhost:8180/docs/changelog.html [snip] 44 % completed (116768 / 260027 bytes) [snip] 100 % completed (260027 / 260027 bytes) urlget2の非同期通信中にエラーが発生した場合でも、プログラムの終了コードは0となってしまう。 実行ループ終了後にDelegateから通信成功/失敗のステータスを取得できるようにし、失敗していた場合に終了コード3となるようにプログラムを修正せよ。 logincheck.mは会議室予約システムのアカウントログイン確認プログラムである。 http://localhost:8180/roomReservation/login2.jspをターゲットに引数で指定されたuser_nameとmail_addressをPOSTメソッドで送信する。 ログイン後の遷移先コンテンツに文字列「List of rooms」が含まれるか否かでログイン成否を判断している。 現状このプログラムは未完成であるが、正しく動作するようプログラムを修正せよ。 $ ./logincheck iwasaki iwasaki@freebsd.org |