| ||||||||
| ||||||||
| ||||||||
|
Device Driver
Device DriverはKinetic 2915 PCIを動かすためのもので、RT-Linuxローダブルモジュールの形をとっている。
RT-Linuxとしての機能を使用している部分はこのDevice Driverで、babarlDAQの心臓部となっている。 Device
Driverと言っても汎用性はなく、随時書き直して使用する必要がある。基本的な部分はライブラリ、雛形を用意してあるので、それをもとに書き直せばよい。
Device
Driverは収集したデータを1Block(16KBytes)バッファリングしておき、データが溜まったらTransferがそのデータを他の構成要素に転送する。通常RT-Linuxローダブルモジュールと通常のプロセスとの通信にはRT-FIFOと呼ばれる専用のFIFO(First
In First Out)を使用するが、babarlDAQではバッファ用のメモリをあらかじめ確保してRT-FIFOを使わないで通信を行っている。
実現している機能
Device Driverの動作
Device
DriverはCollectorによってロードされる。モジュールがロードされた瞬間からデータ収集が開始される。この時点ですでにTransferもCollectorによって起動されており、Transferと通信しつつCAMACからデータを収集する。データ収集はCollectorによりDevice
Driverが取り除かれることで停止し、同時にTransferも終了する。
Interrupt RequestとPolling
イベントを処理する方法としてInterrupt
Requestで割り込みをかけるものと、Pollingで監視するものを選択できる。
割り込みを使用する場合は
void intr_handler(void){ /* イベント処理 */ } . . . int init_module(void){ . . rfs_enable_interrupt(); // Enable Interrupt (PCI Card) request_RTirq(IRQ,intr_handler); // Reqest IRQ return 0; }
のように簡潔に記述することができるが、Driverとユーザープロセスとの同期をとるのが難しいため、 Single
Bufferしか扱えない。
Pollingで監視する場合は、Pollingの周期毎にCreate
ControllerのLAMをチェックすればよいのだが、 K3922 + K2915の組み合わせでのCAMAC Single
Functionはえらい時間がかかる。(8us程度) なのでPCI
Cardの割り込みは許可し、Driverではその割り込みをハンドリングしないようにしておいて、 PCI
CardのRFSをチェックすることで1us程度でLAMをチェックできる。
Pollingの周期はとりあえず50us周期にしてある。過去にPentium
90MHz Linux Kernel
2.2.33のマシンでテストしたところ、25us周期まで耐えることができたが、あまり周期を早くしても、ユーザープロセスが動作できる時間が少なくなるため、50us周期というのが妥当な数字かと思う。
PollingのコーディングはKernel
2.0.*のときはrt_task_make_periodicという関数で行っていたが、2.2.*では興味深いことにpthreadを使う。
void *daq_handler(void *t){ while(1){ pthread_wait_np(); // Wait for next period . . } } int init_module(void){ struct sched_param p; hrtime_t now = gethrtime(); // Get Now Time . . rfs_enable_interrupt(); // Enable Interrupt pthread_create(&daqtask,NULL,daq_handler,(void *)1); // Create Thread pthread_make_periodic_np(daqtask,now+100000,50000); // Set period p.sched_priority = 1; // Set Priority pthread_setschedparam(daqtask,SCHED_FIFO,&p); // Set Schedule Parameter return 0; }
Pollingを使用するとユーザープロセスとの同期をとるのが難しくないため、Double
Bufferが使える。ユーザープロセスが片方のBufferを転送している間にも、データ収集を行うことができるのでパフォーマンスをよくすることができる。パフォーマンス、安定性、危険度から考えてPollingを使用することをお勧めする。
ただし、コンピュータ1台でデータ収集を行う場合はユーザープロセスにかけられる時間が十分ほしいので、割り込みを使用した方が効率的かもしれない。
Device DriverとTransferとの通信 (Single Buffer
with RT-FIFO)
バッファに使うMemoryを確保するためにLinuxをappend="xxxm"のオプション付きで起動する。例えば全体でMemoryが128MBあり、2MBの空きを確保するにはappend="126m"を指定する。
Device
Driver側からアクセス
#define ADDRESS (126*0x100000) char *rt_ptr; rt_ptr = __va(ADDRESS);
とすれば、直接rt_ptrを使ってアクセスできる。
Transfer側からアクセス
#define ADDRESS (126*0x100000) int fdm; char *user_ptr; fdm = open("/dev/mem",O_RDONLY); user_ptr = (char *)mmap(0,0x4004,PROT_READ,MAP_FILE|MAP_PRIVATE,fdm,ADDRESS);
とすれば、user_ptrを使ってアクセスできる。
バッファが溜まるとDevice
DriverはFlagをたて、FIFOハンドラ(RT-Linuxのページ参照)を登録して休止する。
TransferではこのFlagを常に監視しており、Flagがたてば即座にデータを転送し、その後Device
DriverのFIFOハンドラを解除する。こうすることで安定に、高速にデータ転送を行なっている。
Device DriverとTransferとの通信 (Double Buffer without
RT-FIFO)
バッファに使うMemoryを確保するためにLinuxをappend="xxxm"のオプション付きで起動するのは
Single Buffer時と同様である。
Device Driver側からアクセス (Single Bufferと同様)
#define ADDRESS (126*0x100000) char *rt_ptr; rt_ptr = __va(ADDRESS);
とすれば、直接rt_ptrを使ってアクセスできる。
Transfer側からアクセス
#define ADDRESS (126*0x100000) int fdm; char *user_ptr; fdm = open("/dev/mem",O_RDWR); user_ptr = (char *)mmap(0,0x8008,PROT_READ|PROT_WRITE,MAP_FILE|MAP_SHARED,fdm,ADDRESS);
とすれば、user_ptrを使ってアクセス(読み書き)できる。
Driver側はバッファが溜まるとそのバッファのFlagをたて次のBufferにデータを格納する。
2つのバッファにFlagがたっている場合はなにもしないで待機をする。
Transfer側は常にDriverがたてるFlagを監視し、Flagがたったら即座にデータ転送をする。データ転送が終了すればFlagを解除する。
このようにして同期をとり、デッドタイムを少なくしてデータ転送を行うことができる。
Storageの書き込みさえ間に合えば、データ転送によるデッドタイムは生じない。
TransferはDevice Driverで収集したデータをDatabaseとRecorder(データ保存するRUNのみ)に転送するプロセスである。
Recorderには転送が確実なTCP、DatabaseにはUDPプロトコルをそれぞれ使用して転送する。 Transferの動作はほとんどDevice
Driverの頁で述べられているので省略する。
Transferはmmapで/dev/memを使ったりnice(-20)でプロセスの優先順位を上げているので、rootでコンパイルしてsuidをたてておく必要がある。
Database
DatabaseはRunの情報を管理するプロセスである。 Run Number、Start Timeなどの静的な情報だけでなく、最新のRaw
Data(1Block)やテープ残量の動的な情報も有している。 Databaseは複数のDataserverへRaw
Dataを配信することができ、複数のマシンでオンライン解析が可能である。
Databaseの実体
Databaseのプロセスは以下の4つの非同期に発生するイベントを処理しなければならない。
これらを満たすため、上記の1、3、4についてはスレッドを生成して処理している。 2だけメインループで処理している。
/* Control commnads */ void controlThread(void){ if((contaccsock = accept(contsock,(struct sockaddr *)&contcaddr,&len)) < 0){ perror("Error in accept contaccsock.\n"); exit(1); } close(contsock); while(1){ recv(contaccsock,recvbuf,1024,MSG_WAITALL); controllCommand(recvbuf[0]); bzero(recvbuf,1024); bzero(sendbuf,1024); } close(contaccsock); } /* Disk Used */ void diskThread(void){ unsigned char diskbuf[8]; while(1){ recvfrom(disksock,diskbuf,8,0,(struct sockaddr *)&caddr,&listlen); memcpy((char *)&dynamic_data.fulldisk,diskbuf,4); memcpy((char *)&dynamic_data.useddisk,diskbuf+4,4); } } /* List Data */ void listThread(void){ /* List Data Socket */ while(1){ recvfrom(listsock,dynamic_data.listdata,datasize,0,(struct sockaddr *)&caddr,&listlen); multisend((char *)&dynamic_data.listdata); } } . . . int main(int argc,char *argv[]){ pthread_t controlthread,diskthread,listthread; . . . /* Create Thread */ pthread_create(&controlthread,NULL,(void *)&controlThread,NULL); pthread_create(&diskthread,NULL,(void *)&diskThread,NULL); pthread_create(&listthread,NULL,(void *)&listThread,NULL); . . . }
Controller
Controllerはデータ収集のコントローラである。データ収集系のプロセスの内では唯一ユーザーインターフェイスを有している。
Xが動いていない状態で動作させたいので、キャラクターベースで作ってある。キャラクターベースということで、ncursesライブラリを使用している。
Controllerのコマンド
Controller 起動時のオプション
Collector
CollectorはControllerと通信し、Device DriverをロードしたりTransferを起動するためのプロセスである。
Collector自身はただControllerとDevice Driver、Transferの橋渡しのためだけに存在する。
Recorder
Recorderはデータ保存のためのプロセスである。
SCSIテープデバイスとハードディスクに書き込みをすることができる。また、テープ書き込み時にはテープ残量の情報をDatabaseに配信する。
SCSIテープデバイスが2台ある場合、2本同時書き込みにも対応している。
Analyzer
AnalyzerはAnapawやBlkmonitorなどのオンライン解析、ステータスモニタにデータを渡すためのプロセスである。共有メモリ、セマフォを使用することで同時に複数のオンライン解析、ステータスモニタからアクセス可能になっている。
Shared Memoryに関する記述
#define SHMKEY 17001 . . . int main(int argc,char *argv[]){ . . . if((shmid = shmget(SHMKEY,0x4004,IPC_CREAT|0777)) == -1){ perror("Can't create shared memory.\n"); exit(1); } shmp = shmat(shmid,0,0); . . . }
Semaphoreに関する記述
#define SEMKEY 18001 /* Semaphore Interface */ /* Lock */ void semaphore_p(void){ semb.sem_op = -1; semop(semid,&semb,1); } /* Unlock */ void semaphore_v(void){ semb.sem_op = 1; semop(semid,&semb,1); } . . . int main(int argc,char *argv[]){ . . . if((semid = semget(SEMKEY,1,IPC_CREAT|0666)) == -1){ perror("Can't create semaphore.\n"); exit(1); } semunion.val = 1; if(semctl(semid,0,SETVAL,semunion) == -1){ perror("Can't control semaphore.\n"); semctl(semid,0,IPC_RMID,semunion); exit(1); } . . . }
Anapaw
AnapawはPAWベースのAnalysライクなオンライン解析プログラムである。(オフラインもできる)
Analyzerのプロセスが同一コンピュータ上で動作していればオンラインでデータ解析が可能である。
このAnapaw本体はばばではなく先輩の武内さんが作っているので、詳細は彼に書いてもらうとする。
詳細はこちら
Last Update: 2005/4/7
Hidetada Baba
baba @ rarfaxp.riken.jp