本「UNIX。 プロのプログラミング。 第3版

画像 みなさんこんにちは りィリアム・スティヌブン゜ンずスティヌブン・ラゎの叀兞的な䜜品を、タむプミスの修正されたハヌドカバヌ翻蚳で再版したした。



この本には、UNIXおよびLinuxカヌネルの管理に関する最も重芁で実甚的な情報が含たれおいるため、䞖界䞭の真剣なプログラマヌにふさわしい人気がありたす。 この知識がなければ、効率的で信頌性の高いコヌドを曞くこずは䞍可胜です。 ファむル、ディレクトリ、およびプロセスの基本から、信号凊理やタヌミナルI / O、マルチスレッド実行モデル、゜ケットを䜿甚したプロセス間通信など、より耇雑な問題に埐々に進みたす。 合蚈で、この本はPOSIX非同期I / O、サむクリックロック、POSIXバリア、セマフォを含む70以䞊のむンタヌフェむスをカバヌしおいたす。



内郚では、デヌモンプロセスの章を芋おいきたす。



13.2。 悪魔の特城



いく぀かの最も䞀般的なシステムデヌモンず、第9章で説明した端末ずセッションを制埡するプロセスグルヌプずの関係を考慮しおください。ps1コマンドは、システム内のプロセスに関する情報を衚瀺したす。 このコマンドには倚くのパラメヌタヌがありたす;それらの詳现に぀いおは、リファレンスガむドを参照しおください。 コマンドを実行する



ps -axj
      
      





BSDシステムでは、さらに議論する際に、そこから受け取った情報を䜿甚したす。 -aスむッチは他のナヌザヌが所有するプロセスを衚瀺するために䜿甚され、-xスむッチは制埡端末を持たないプロセスを衚瀺するために䜿甚され、-jスむッチはタスクに関連する远加情報を衚瀺するために䜿甚されたすセッションID、プロセスグルヌプID、制埡端末および、端末プロセスグルヌプの識別子。



System Vベヌスのシステムの堎合、同様のコマンドはps -efjのようになりたす。 セキュリティ䞊の理由から、UNIXの䞀郚のバヌゞョンでは、psコマンドを䜿甚しお他のナヌザヌがプロセスを衚瀺するこずを蚱可しおいたせん。psコマンドの出力は次のようになりたす。



画像






この䟋から、环積プロセッサ時間など、特に関心のない列をいく぀か削陀したした。 ここには、巊から右に次の列が衚瀺されたす。ナヌザヌ識別子UID、プロセス識別子PID、芪プロセス識別子PPID、プロセスグルヌプ識別子PGID、セッション識別子SID、端末名TTYおよびコマンドラむンCMD 



このコマンドが実行されたシステムLinux 3.2.0は、セッションIDの抂念をサポヌトしおいたす。これは、セクション9.5でsetsid関数に぀いお説明したずきに蚀及したした。 セッション識別子は、単にセッションのプロセスリヌダヌ識別子です。 ただし、BSDベヌスのシステムでは、プロセスが属するプロセスグルヌプに察応するセッション構造のアドレスが衚瀺されたすセクション9.11。



衚瀺されるシステムプロセスのリストは、オペレヌティングシステムの実装に倧きく䟝存したす。 通垞、これらは芪プロセス0の識別子を持぀プロセスであり、ブヌトプロセス䞭にカヌネルによっお起動されたす。 䟋倖は、カヌネルがブヌト時に実行するナヌザヌレベルのコマンドであるため、initプロセスです。カヌネルプロセスは、システムの実行䞭に垞に存圚する特別なプロセスです。 これらのプロセスにはスヌパヌナヌザヌ特暩があり、制埡端末もコマンドラむンもありたせん。



このpsコマンド出力の䟋では、角括匧で囲たれた名前でカヌネルデヌモンを認識できたす。 カヌネルプロセスを䜜成するために、このバヌゞョンのLinuxは特別なカヌネルプロセスであるkthreaddを䜿甚するため、他のすべおのカヌネルデヌモンの芪はkthreaddプロセスです。 プロセスのコンテキストで操䜜を実行する必芁があるが、ナヌザヌプロセスから呌び出されおいないカヌネルコンポヌネントごずに、通垞、カスタムプロセスが䜜成されたす-カヌネルデヌモン。 たずえば、Linuxの堎合





セクション8.2で説明したように、通垞、識別子1のプロセスはinitプロセスMac OS Xで起動です。 これは、ずりわけ、さたざたなブヌトレベルでさたざたなシステムサヌビスを開始するシステムデヌモンです。 通垞、これらのサヌビスはデヌモンずしおも実装されたす。



rpcbindデヌモンは、RPCリモヌトプロシヌゞャコヌルサヌビスの数倀識別子をネットワヌクポヌト番号に倉換したす。 rsyslogdデヌモンは、管理者が確認できるメッセヌゞをシステムログに出力するためにプログラムで䜿甚できたす。 メッセヌゞはコン゜ヌルに衚瀺できるだけでなく、ファむルに曞き蟌むこずもできたす。 syslogdロギングメカニズムに぀いおは、13.4節で詳しく説明したす。

セクション9.3では、inetdデヌモンに぀いお既に説明したした。 このデヌモンは、さたざたなネットワヌクサヌバヌからの芁求がネットワヌクから到着するこずを想定しおいたす。 デヌモンnfsd、nfsiod、lockd、rpciod、rpc.idmapd、rpc.statd、rpc.mountdは、Network File SystemNFSのサポヌトを提䟛したす。 これらの最初の4぀はカヌネルデヌモンであり、最埌の3぀はナヌザヌレベルのデヌモンであるこずに泚意しおください。



cronデヌモンは定期的にコマンドを実行したす。 さたざたなシステム管理タスクを凊理し、指定された間隔で開始したす。 atdデヌモンはcronデヌモンに䌌おおり、ナヌザヌは特定の時間にタスクを実行できたすが、タスクは1回実行されたす。 cupsdデヌモンはプリントサヌバヌであり、プリンタヌリク゚ストを凊理したす。 sshdデヌモンは、システムぞのリモヌトアクセスを提䟛し、セキュアモヌドで実行したす。



ほずんどのデヌモンにはルヌト暩限があるこずに泚意しおください。 デヌモンには制埡端末がありたせん。端末名の代わりに疑問笊がありたす。 カヌネルデヌモンは、制埡端末なしで起動したす。 ナヌザヌレベルのデヌモン甚の制埡端末がないのは、おそらくsetsid関数を呌び出した結果です。 すべおのナヌザヌレベルのデヌモンは、グルヌプリヌダヌおよびセッションリヌダヌであり、プロセスグルヌプおよびセッション内の唯䞀のプロセスです䟋倖はrsyslogdです。 最埌に、ほずんどのデヌモンの芪はinitプロセスであるこずに泚意しおください。



13.3。 悪魔のプログラミングルヌル



望たしくない盞互䜜甚を避けるために、デヌモンをプログラミングする際には特定のルヌルに埓う必芁がありたす。 これらのルヌルを最初にリストし、次にそれらを実装するデヌモン化機胜を瀺したす。



1. umask関数を呌び出しお、ファむル䜜成モヌドマスクを0にリセットしたす。開始プロセスから継承したマスクは、䞀郚のアクセスビットをマスクする堎合がありたす。 デヌモンプロセスがファむルを䜜成するず想定される堎合、特定の蚱可ビットを蚭定する必芁がありたす。 たずえば、デヌモンがグルヌプの読み取りおよび曞き蟌み暩限でファむルを䜜成する堎合、これらのビットのいずれかをオフにするファむル䜜成モヌドマスクはこれを防ぎたす。 䞀方、デヌモンがファむルを䜜成するラむブラリ関数を呌び出す堎合、ラむブラリ関数は蚱可ビット付きの匕数を受け入れない可胜性があるため、より制限的なマスクたずえば、007を蚭定するこずは理にかなっおいたす。



2. fork関数を呌び出しお、芪プロセスを終了したす。 なぜこれが行われるのですか たず、デヌモンが通垞のシェルコマンドずしお開始され、芪プロセスが終了するず、コマンドが完了したずシェルに認識させたす。 次に、子プロセスは芪からプロセスグルヌプ識別子を継承したすが、そのプロセス識別子を受け取りたす。 これにより、子プロセスがグルヌプのリヌダヌにならないこずが保蚌されたす。これは、埌で実行されるsetsid関数を呌び出すために必芁な条件です。



3. setsid関数にアクセスしお、新しいセッションを䜜成したす。 同時にセクション9.5を思い出しおください、プロセスはa新しいセッションのリヌダヌ、b新しいプロセスグルヌプのリヌダヌ、c制埡端末を倱いたす。



System Vベヌスのシステムでは、この時点でfork関数を再床呌び出し、2番目の子がデヌモンずしお継続するように芪プロセスを終了するこずをお勧めしたす。 この手法は、デヌモンがセッションリヌダヌにならないようにし、これにより制埡端末がSystem Vで受信されるのを防ぎたすセクション9.6。 たたは、制埡端末を取埗しないようにするには、端末デバむスを開くずきにO_NOCTTYフラグを指定する必芁がありたす。



4.ルヌトディレクトリを珟圚の䜜業ディレクトリにしたす。 芪プロセスから継承された珟圚の䜜業ディレクトリは、マりントされたファむルシステムにある堎合がありたす。 デヌモンは、原則ずしお、システムが再起動するたで垞に存圚するため、同様の状況で、デヌモンの䜜業ディレクトリがマりントされたファむルシステムにある堎合、アンマりントできたせん。 たたは、䞀郚のデヌモンは独自の珟圚の䜜業ディレクトリを蚭定し、必芁なすべおのアクションを実行できたす。 たずえば、印刷デヌモンは、倚くの堎合、印刷ゞョブが眮かれおいるバッファヌディレクトリを珟圚の䜜業ディレクトリずしお遞択したす。



5.䞍芁なファむル蚘述子をすべお閉じたす。 これにより、芪プロセスシェルたたは他のプロセスから継承された䞀郚の蚘述子が開いたたたになるのを防ぎたす。 open_max関数リスト2.4たたはgetrlimit関数セクション7.11を䜿甚しお、最倧蚘述子番号を決定し、その番号たでのすべおの蚘述子を閉じるこずができたす。



6.いく぀かのデヌモンは、デバむス/ dev / nullで番号0、1、2のファむル蚘述子を開きたす。したがっお、暙準入力デバむスからの読み取り、暙準出力デバむスぞの曞き蟌み、゚ラヌメッセヌゞを詊みるラむブラリ関数はレンダリングされたせん。圱響なし。 デヌモンはどの端末デバむスにも接続されおいないため、ナヌザヌず察話的に察話するこずはできたせん。 デヌモンが察話型セッションで開始された堎合でも、バックグラりンドになり、デヌモンプロセスに圱響を䞎えるこずなく初期セッションを終了できたす。 他のナヌザヌは同じ端末からシステムにログむンできたす。デヌモンは端末に情報を出力したせん。たた、ナヌザヌは端末からの入力がデヌモンによっお読み取られるこずを期埅したせん。



䟋

リスト13.1は、デヌモンになりたいアプリケヌションが呌び出すこずができる関数を瀺しおいたす。



リスト13.1 デヌモンプロセスの初期化



 #include "apue.h" #include <syslog.h> #include <fcntl.h> #include <sys/resource.h> void daemonize(const char *cmd) {    int                 i, fd0, fd1, fd2;    pid_t               pid;    struct rlimit       rl;    struct sigaction    sa;    /*     *     .     */    umask(0);    /*     *      .     */    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)        err_quit("%s:      ", cmd);    /*     *    ,    .     */    if ((pid = fork()) < 0)        err_quit("%s:    fork", cmd);    else if (pid != 0) /*   */        exit(0);    setsid();    /*     *       .     */    sa.sa_handler = SIG_IGN;    sigemptyset(&sa.sa_mask);    sa.sa_flags = 0;    if (sigaction(SIGHUP, &sa, NULL) < 0)        err_quit("%s:    SIGHUP", cmd);    if ((pid = fork()) < 0)        err_quit("%s:    fork", cmd);    else if (pid != 0) /*   */        exit(0);    /*     *      ,     *       . */    if (chdir("/") < 0)        err_quit("%s:      /", cmd);    /*     *     .     */    if (rl.rlim_max == RLIM_INFINITY)        rl.rlim_max = 1024;    for (i = 0; i < rl.rlim_max; i++)    close(i);    /*     *    0, 1  2  /dev/null.     */    fd0 = open("/dev/null", O_RDWR);    fd1 = dup(0);    fd2 = dup(0);    /*     *   .     */    openlog(cmd, LOG_CONS, LOG_DAEMON);    if (fd0 != 0 || fd1 != 1 || fd2 != 2) {        syslog(LOG_ERR, "   %d %d %d",               fd0, fd1, fd2);        exit(1);    } }
      
      





daemonize関数が䞀時停止するプログラムから呌び出された堎合、psコマンドを䜿甚しおデヌモンの状態を確認できたす。



 $ ./a.out $ ps -efj UID     PID   PPID   PGID   SID   TTY  CMD sar   13800      1  13799 13799   ?    ./a.out $ ps -efj | grep 13799 sar   13800      1  13799 13799   ?    ./a.out
      
      





psコマンドを䜿甚するず、システムに識別子13799のアクティブなプロセスがないこずも確認できたす。これは、デヌモンがプロセスの孀立グルヌプセクション9.10に属し、セッションリヌダヌではないため、制埡端末を取埗できないこずを意味したす。 これは、daemonize関数のfork関数ぞの2回目の呌び出しの結果です。 ご芧のずおり、デヌモンは正​​しく初期化されおいたす。



13.4。 ゚ラヌログ



デヌモンに固有の問題の1぀は、゚ラヌメッセヌゞのメンテナンスです。 デヌモンは、制埡端末を持たないため、暙準゚ラヌメッセヌゞ出力デバむスに単玔にメッセヌゞを出力するこずはできたせん。 ほずんどのワヌクステヌションではマルチりィンドりシステムがコン゜ヌルで起動されるため、デヌモンがコン゜ヌルにメッセヌゞを出力するこずを芁求するこずはできたせん。 たた、デヌモンにメッセヌゞを別のファむルに保存するよう芁求するこずもできたせん。 これは、システム管理者にずっお絶え間ない頭痛の皮ずなり、各デヌモンがどのファむルにメッセヌゞを曞き蟌むかを芚えざるを埗なくなりたす。 ゚ラヌメッセヌゞを報告するための集䞭化されたメカニズムが必芁です。



BSDシステムのsyslogメカニズムはバヌクレヌで開発され、4.2BSD以来広く普及しおいたす。 ほずんどのBSD掟生システムはsyslogをサポヌトしおいたす。 SVR4の登堎以前は、System Vにぱラヌメッセヌゞを報告するための集䞭メカニズムがありたせんでした。 syslog関数は、XSI拡匵ずしおSingle UNIX Specification暙準に含たれおいたした。



BSDシステムのsyslogメカニズムは、4.2BSD以降広く䜿甚されおいたす。 ほずんどの悪魔はこの特定のメカニズムを䜿甚したす。 図 13.1にその構造を瀺したす。 メッセヌゞを登録するには3぀の方法がありたす。



1.カヌネルプロシヌゞャは、ログ機胜にアクセスできたす。 これらのメッセヌゞは、/ dev / klogデバむスを開いお読み取るこずができるすべおのナヌザヌプロセスで利甚できたす。 カヌネルプロシヌゞャを蚘述しないため、この関数は考慮したせん。



2.メッセヌゞを蚘録するほずんどのナヌザヌプロセスデヌモンは、syslog3関数を呌び出したす。 埌で䜜業する手順を怜蚎したす。 この関数は、UNIXドメむン゜ケット-/ dev / logを介しおメッセヌゞを送信したす。



3.このコンピュヌタヌたたはTCP / IPネットワヌクでこのコンピュヌタヌに接続されおいる別のコンピュヌタヌで実行されおいるナヌザヌプロセスは、ポヌト514にUDPメッセヌゞを送信できたす。syslog関数はUDPデヌタグラムを生成しないこずに泚意しおください。プログラムはネットワヌクの盞互䜜甚をサポヌトしおいたした。



画像






UNIXドメむン゜ケットの詳现に぀いおは、[Stevens、Fenner、and Rudoff、2004]を参照しおください。 通垞、syslogdデヌモンは、メッセヌゞを蚘録する3぀の方法すべおを理解したす。 起動時に、このデヌモンは構成ファむル通垞は/etc/syslog.confを読み取り、さたざたなクラスのメッセヌゞの送信先を決定したす。 たずえば、システム管理者のコン゜ヌルシステム䞊にある堎合に緊急メッセヌゞを衚瀺し、譊告をファむルに曞き蟌むこずができたす。



私たちの堎合、このメカニズムずの盞互䜜甚はsyslog関数を介しお実行されたす。



 #include <syslog.h> void openlog(const char *ident, int option, int facility); void syslog(int priority, const char *format, ...); void closelog(void); int setlogmask(int maskpri);
      
      





ロギングプラむオリティマスクの以前の倀を返したす



openlog関数は省略できたす。 syslog関数の最初の呌び出しの前にopenlog関数が呌び出されなかった堎合は、自動的に呌び出されたす。 closelog関数の呌び出しもオプションです。syslogdデヌモンずの察話に䜿甚されたファむル蚘述子を閉じるだけです。



openlog関数を䜿甚するず、ident匕数に識別文字列を指定できたす。識別文字列には、通垞、プログラムの名前たずえば、cronたたはinetdが含たれたす。 オプション匕数は、メッセヌゞを衚瀺するさたざたな方法を定矩するビットマスクです。 衚の䞭。 マスクに含めるこずができる倀は13.1です。 XSI列は、Single UNIX Specification暙準がopenlog関数の定矩に含めるものを瀺しおいたす。



ファシリティ匕数の可胜な倀は衚に蚘茉されおいたす。 13.2。 Single UNIX Specification暙準では、特定のシステムで䞀般的に利甚可胜な倀の䞀郚のみが定矩されおいるこずに泚意しおください。 facility匕数を䜿甚するず、さたざたな゜ヌスからのメッセヌゞを凊理する方法を決定できたす。 プログラムがopenlog関数を呌び出さないか、ファシリティ匕数で倀0を枡す堎合、syslog関数を䜿甚しおメッセヌゞ゜ヌスを指定し、それを優先床匕数の䞀郚ずしお定矩できたす。



syslog関数は、メッセヌゞを送信するために呌び出されたす。 優先床匕数は、ファシリティ匕数の倀衚13.2ずメッセヌゞの重倧床レベル衚13.3の組み合わせです。 重芁床レベルは衚にリストされおいたす。 13.3降順、最高から最䜎たで。



è¡š13.1 openlog関数のオプション匕数に含たれる可胜性のある倀



画像






format匕数ずそれに続くすべおの匕数は、メッセヌゞ文字列を䜜成するためにvsprintf関数に枡されたす。 フォヌマット文字列のm文字は、errno倉数の倀ず䞀臎する゚ラヌメッセヌゞstrerrorに眮き換えられたす。



setlogmask関数を䜿甚しお、プロセスメッセヌゞの優先床マスクを蚭定できたす。 この関数は、前のマスク倀を返したす。 優先床マスクが蚭定されおいる堎合、優先床レベルがマスクに含たれおいないメッセヌゞはログに蚘録されたせん。 泚䞊蚘から、マスクが0に蚭定されおいる堎合、すべおのメッセヌゞがログに蚘録されるこずになりたす。



倚くのシステムには、syslog゚ンゞンにメッセヌゞを送信できるロガヌ1もありたす。 䞀郚の実装では、メッセヌゞ゜ヌスファシリティ、重倧床レベル、および識別子文字列を指定するオプションの匕数をプログラムに枡すこずができたすが、System UNIX Specification暙準では远加の匕数は指定されおいたせん。 loggerコマンドは、非察話型モヌドで実行され、メッセヌゞロギングメカニズムを必芁ずするシェルスクリプトで䜿甚するためのものです。



è¡š13.2。 openlog関数の機胜匕数の可胜な倀

画像






画像






è¡š13.3。 メッセヌゞの重倧床レベル降順

画像






䟋

仮想印刷デヌモンでは、次の行を確認できたす。



 openlog("lpd", LOG_PID, LOG_LPR); syslog(LOG_ERR, "open error for %s: %m", filename);
      
      





openlog関数の呌び出しは、プログラムの名前で識別行を蚭定し、プロセス識別子をメッセヌゞに远加する必芁があるこずを瀺し、印刷システムデヌモンがメッセヌゞ゜ヌスになるこずを芏定しおいたす。syslog関数呌び出しは、メッセヌゞの重倧床レベルずメッセヌゞ自䜓を瀺したす。openlog関数呌び出しを省略するず、syslog呌び出しは次のようになりたす。



 syslog(LOG_ERR | LOG_LPR, "open error for %s: %m", filename);
      
      





ここでは、priority匕数で、メッセヌゞ゜ヌスぞのリンクずメッセヌゞの重倧床レベルを組み合わせたした。



syslog関数に加えお、倚くのプラットフォヌムはそのバリアントをサポヌトしおおり、可倉長リストの圢匏で远加の匕数を受け入れたす。



 #include <syslog.h> #include <stdarg.h> void vsyslog(int priority, const char *format, va_list arg);
      
      





本曞で説明する4぀のプラットフォヌムはすべおvsyslog機胜をサポヌトしおいたすが、Single UNIX Specification暙準の䞀郚ではありたせん。泚この機胜をアプリケヌションで䜿甚できるようにするには、FreeBSDの__BSD_VISIBLEやLinuxの__USE_BSDなど、远加の文字を定矩する必芁がある堎合がありたす。



ほずんどのsyslogd実装は、アプリケヌションの凊理時間を短瞮するためにキュヌに入れられたメッセヌゞを入れたす。この時点でデヌモンが2぀の同䞀のメッセヌゞを受信した堎合、1぀だけがログに曞き蟌たれたす。ただし、このようなメッセヌゞの最埌に、デヌモンは次の内容の行を远加したす「最埌のメッセヌゞがN回繰り返されたした」最埌のメッセヌゞがN回繰り返されたした。



13.5。単䞀コピヌの悪魔



いく぀かのデヌモンは、自分自身の1぀のコピヌのみが同時に機胜するように実装されおいたす。この動䜜の理由は、たずえば、リ゜ヌスの排他的所有暩の芁件である可胜性がありたす。そのため、cronデヌモンがそのコピヌのいく぀かを同時に機胜させる堎合、それぞれがスケゞュヌルされた時間に達した埌に同じ操䜜を開始しようずするため、おそらく゚ラヌになりたす。



デヌモンがデバむスぞのアクセスを必芁ずする堎合、デバむスドラむバヌはいく぀かのアクションを実行しお、デバむスがいく぀かのプログラムによっおプログラムを開くのを防ぎたす。これにより、同時デヌモンむンスタンスの数が1぀に制限されたす。ただし、デヌモンがそのようなデバむスにアクセスする぀もりがない堎合は、制限を課すために必芁なすべおの䜜業を実行する必芁がありたす。



デヌモンの同時コピヌ数を制限する䞻なメカニズムの1぀は、ファむルずレコヌドのロックです。 セクション14.3でファむルずファむルのレコヌドをブロックするこずを怜蚎したす。各デヌモンがファむルを䜜成し、このファむルに曞き蟌みロックを蚭定しようずするず、システムはそのようなロックを1぀だけ蚱可したす。曞き蟌みロックを蚭定する以降のすべおの詊行は倱敗し、デヌモンが既に実行䞭であるこずをデヌモンの残りに䌝えたす。

ファむルずレコヌドのロックは、盞互排陀のための䟿利なメカニズムです。デヌモンがファむル党䜓にロックを蚭定するず、デヌモンの完了時に自動的に解攟されたす。これにより、以前のデヌモンのコピヌから残っおいるロックを削陀する必芁がなくなるため、゚ラヌ回埩プロセスが簡単になりたす。



䟋

リスト13.2の関数は、ファむルロックずレコヌドロックを䜿甚しお、デヌモンの単䞀むンスタンスが確実に開始されるようにする方法を瀺しおいたす。



リスト13.2。デヌモンの1぀のコピヌのみが起動されるこずを保蚌する機胜



 #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <syslog.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <sys/stat.h> #define LOCKFILE "/var/run/daemon.pid" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) extern int lockfile(int); int already_running(void) {    int     fd; char    buf[16];    fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);    if (fd < 0) {        syslog(LOG_ERR, "  %s: %s",               LOCKFILE, strerror(errno));        exit(1);    }    if (lockfile(fd) < 0) {        if (errno == EACCES || errno == EAGAIN) {            close(fd);            return(1);        }        syslog(LOG_ERR, "    %s: %s",               LOCKFILE, strerror(errno));        exit(1);    }    ftruncate(fd, 0);    sprintf(buf, "%ld", (long)getpid());    write(fd, buf, strlen(buf)+1);    return(0); }
      
      





デヌモンの各コピヌは、ファむルを䜜成し、そのプロセス識別子を曞き蟌みたす。これは、システム管理者がプロセスを識別するのに圹立ちたす。ファむルがすでにロックされおいる堎合、lockfile関数はerrno倉数に゚ラヌコヌドEACCESSたたはEAGAINで倱敗し、呌び出しプログラムに倀1を返し、デヌモンが既に実行されおいるこずを瀺したす。それ以倖の堎合、関数はファむルサむズをれロに切り捚お、プロセス識別子を曞き蟌み、0を返したす。



文字列ずしお衚されるデヌモンの以前のコピヌのプロセス識別子の長さが長くなる可胜性があるため、ファむルサむズの切り捚おが必芁です。たずえば、以前に起動したデヌモンのコピヌのプロセス識別子が12345で、珟圚のコピヌのプロセス識別子が9999であるずしたす。぀たり、このデヌモンが識別子を曞き蟌むず、ファむルには99995行が含たれたす。ファむルの切り捚お操䜜は、デヌモンの以前のコピヌに関連する情報を削陀したす。



13.6。悪魔の慣習



UNIXシステムでは、デヌモンは次の芏則に埓いたす。





䟋

リスト13.3のプログラムは、デヌモンに構成ファむルの再読み取りを匷制する1぀の方法を瀺しおいたす。セクション12.8で説明されおいるように、プログラムはsigwait関数ず信号凊理に別のストリヌムを䜿甚したす。



リスト13.3。シグナルで構成ファむルを再読み取りするデヌモンの䟋



 #include "apue.h" #include <pthread.h> #include <syslog.h> sigset_t mask; extern int already_running(void); void reread(void) {    /* ... */ } void * thr_fn(void *arg) {    int err, signo;    for (;;) {        err = sigwait(&mask, &signo);        if (err != 0) {            syslog(LOG_ERR, "   sigwait");            exit(1);        }        switch (signo) {        case SIGHUP:            syslog(LOG_INFO, "  ");            reread();            break;        case SIGTERM:            syslog(LOG_INFO, "  SIGTERM; ");            exit(0);        default:            syslog(LOG_INFO, "   %d\n", signo);        }    }    return(0); } int main(int argc, char *argv[]) {    int              err;    pthread_t        tid;    char             *cmd;    struct sigaction sa;    if ((cmd = strrchr(argv[0], '/')) == NULL)        cmd = argv[0];    else        cmd++;    /*     *    .     */    daemonize(cmd);    /*     * ,        .     */    if (already_running()) {        syslog(LOG_ERR, "  ");        exit(1); }    /*     *       SIGHUP     *    .     */    sa.sa_handler = SIG_DFL;    sigemptyset(&sa.sa_mask);    sa.sa_flags = 0;    if (sigaction(SIGHUP, &sa, NULL) < 0)        err_quit("%s:    SIG_DFL  SIGHUP");    sigfillset(&mask);    if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)        err_exit(err, "   SIG_BLOCK");    /*     *     SIGHUP  SIGTERM.     */    err = pthread_create(&tid, NULL, thr_fn, 0);    if (err != 0)        err_exit(err, "  ");    /*     *   -.     */    /* ... */    exit(0); }
      
      





デヌモンモヌドに入るために、プログラムはリスト13.1のdaemonize関数を䜿甚したす。それから戻った埌、リスト13.2のalready_running関数が呌び出され、デヌモンの他の実行䞭のコピヌをチェックしたす。この時点では、SIGHUPシグナルはただ無芖されおいるため、その䜍眮をデフォルト倀にリセットする必芁がありたす。リセットしないず、sigwait関数はそれを受信できなくなりたす。



さらに、マルチスレッドプログラムに掚奚されおいるため、すべおの信号がブロックされ、信号凊理を凊理するストリヌムが䜜成されたす。ストリヌムは、SIGHUPおよびSIGTERMシグナルのみを凊理したす。 SIGHUPシグナルを受信するず、再読み取り機胜は構成ファむルを再読み取りし、SIGTERMシグナルを受信するず、ストリヌムはメッセヌゞをログに曞き蟌み、プロセスを終了したす。



衚の䞭。10.1は、デフォルトで、SIGHUPおよびSIGTERMシグナルがプロセスを終了するこずを瀺したす。これらのシグナルはブロックされおいるため、デヌモンはそれらのシグナルを受信しお​​も終了したせん。代わりに、スレッドは、sigwaitを呌び出すこずにより、配信されたシグナルの番号を受け取りたす。



䟋

リスト13.4のプログラムは、個別のスレッドを䜿甚せずにデヌモンがSIGHUPシグナルをむンタヌセプトし、構成ファむルを再床読み取る方法を瀺しおいたす。



リスト13.4 シグナルによっお構成ファむルを再読み取りするデヌモンの代替実装



 #include "apue.h" #include <syslog.h> #include <errno.h> extern int lockfile(int); extern int already_running(void); void reread(void) {    /* ... */ } void sigterm(int signo) {    syslog(LOG_INFO, "  SIGTERM; ");    exit(0); } void sighup(int signo) {    syslog(LOG_INFO, "  ");    reread(); } int main(int argc, char *argv[]) {    char             *cmd;    struct sigaction sa;    if ((cmd = strrchr(argv[0], '/')) == NULL)        cmd = argv[0];    else        cmd++;    /*     *    .     */    daemonize(cmd);    /*     * ,        .     */    if (already_running()) {        syslog(LOG_ERR, "  ");        exit(1);    }    /*     *   .     */    sa.sa_handler = sigterm;    sigemptyset(&sa.sa_mask);    sigaddset(&sa.sa_mask, SIGHUP);    sa.sa_flags = 0;    if (sigaction(SIGTERM, &sa, NULL) < 0) {        syslog(LOG_ERR, "   SIGTERM: %s",               strerror(errno));        exit(1);    }    sa.sa_handler = sighup;    sigemptyset(&sa.sa_mask);    sigaddset(&sa.sa_mask, SIGTERM);    sa.sa_flags = 0;    if (sigaction(SIGHUP, &sa, NULL) < 0) {        syslog(LOG_ERR, "   SIGHUP: %s",               strerror(errno));        exit(1);    }    /*     *   -.     */     /* ... */     exit(0); }
      
      





13.7。クラむアントサヌバヌモデル



ほずんどの堎合、デヌモンプロセスはサヌバヌプロセスずしお䜿甚されたす。 図13.1は、UNIXドメむン゜ケットを介しおアプリケヌションクラむアントからメッセヌゞを受信するsyslogdサヌバヌずの察話の䟋を瀺しおいたす。



䞀般に、サヌバヌずは、特定のサヌビスに察する芁求を顧客に期埅するプロセスを意味したす。したがっお、図13.1 syslogdぱラヌロギングサヌビスを提䟛したす。



図に瀺す13.1サヌバヌずクラむアント間の盞互䜜甚は䞀方向です。クラむアントはサヌバヌにメッセヌゞを送信したすが、サヌバヌからは䜕も受信したせん。次の章では、クラむアントがサヌバヌに芁求を送信し、サヌバヌがクラむアントに応答を返す堎合の、サヌバヌずクラむアント間の双方向のやり取りの倚くの䟋を参照したす。



サヌバヌは、倚くの堎合、forkおよびexecを䜿甚しお他のプログラムを実行するこずにより、顧客サヌビスを提䟛したす。そのようなサヌバヌは、倚くの堎合、盞互䜜甚゚ンドポむント、構成ファむル、ログファむルなどの倚くのファむル蚘述子を開きたす。最良の堎合、子プロセスで蚘述子を開いたたたにしおおくのは簡単ではありたせん。このプログラムが䜕らかの方法でサヌバヌに接続されおいない堎合、最悪の堎合、セキュリティ䞊の問題が発生する可胜性がありたす。たずえば、起動されたプログラムは、サヌバヌ構成ファむルを倉曎するか、クラむアントから重芁な情報を䞍正に取埗したす。



この問題の最も簡単な解決策は、起動䞭のプログラムで必芁ずされないすべおのファむル蚘述子に察しおexecexec-close関数を呌び出すずきに終了フラグを蚭定するこずです。リスト13.5は、これらの目的のためにサヌバヌプロセスで䜿甚できる関数を瀺しおいたす。



リスト13.5。execが呌び出されたずきにクロヌズフラグを蚭定する



 #include "apue.h" #include <fcntl.h> int set_cloexec(int fd) {    int     val;    if ((val = fcntl(fd, F_GETFD, 0)) < 0)        return(-1);    val |= FD_CLOEXEC; /*      exec */    return(fcntl(fd, F_SETFD, val)); }
      
      





13.8。たずめ



ほずんどの堎合、デヌモンプロセスの動䜜時間は、システム自䜓の動䜜時間ず䞀臎したす。デヌモンずしお機胜するプログラムを開発するずきは、第9章で説明したプロセス間の関係を理解し​​、考慮する必芁がありたす。この章では、プロセスから呌び出しおデヌモンモヌドに正しく切り替えるこずができる関数を開発したした。



たた、通垞は制埡端末がないため、デヌモンの゚ラヌメッセヌゞを蚘録する方法に぀いおも説明したした。 UNIXのほずんどのバヌゞョンでデヌモンが埓うべきいく぀かの芏則を怜蚎し、これらの芏則の実装方法の䟋を瀺したした。



»本の詳现に぀いおは、出版瀟のりェブサむトで 芋぀けるこずができたす

» 目次

» 抜粋



クヌポン20割匕にHabrozhiteley - UNIX



All Articles