linuxでsleepの詳細及び簡単な実例を実現する。

4747 ワード

linuxでsleepの詳細及び簡単な実例を実現する。
sleep:
通常バージョン
1、基本設計の考え方:
   1>SIGALRM信号の処理関数を登録する。
   2>アラムを呼び出してアラームを設定します。
   3>pauseを使用して待つと、カーネルが別のプロセスに切り替わります。
   4>nsecs秒後、アラームタイムアウト、カーネルSIGALRMはこのプロセスに与えます。
   5>カーネル状態からこのプロセスの使用状態に戻る前に未決信号を処理したら、SIGALRM信号があり、その処理関数はsig_であることが分かりました。alrm;
   6>ユーザー状態に切り替えてsig_を実行します。alrm関数、入入入sig_alrm関数時SIGALRM信号は自主的に遮蔽され、sig_からalrm関数はSIGALRM信号自主動作でシールド解除を返します。その後、執動システムからsigreturnを呼び出して再びカーネルに入り、ユーザー状態に戻ってプロセスを継続する主制御プロセス(main関数が適用するmysleep関数)。
   7>pause関数は-1に戻り、次にアラム(0)を使ってアラームをキャンセルし、SIGALRM信号を元に戻す処理動作を調整します。
2、実現コード

#include<stdio.h> 
#include<signal.h> 
 
void handler(int signo) 
{} 
 
int mysleep(int timeout) 
{ 
  struct sigaction act,oact; 
  act.sa_handler = handler; 
  act.sa_flags = 0; 
  sigemptyset(&act.sa_mask); 
 
  sigaction(SIGALRM,&act,&oact); 
  alarm(timeout); 
  pause(); 
  int ret = alarm(0); 
  sigaction(SIGALRM,&oact,NULL); 
  return ret; 
} 
 
int main() 
{ 
  while(1) 
  { 
    printf("using musleep!
"); mysleep(3); } return 0; }
相関関数の分析:

#include <unistd.h> 
int pause(void); 
pause関数は、信号が届くまで、プロセスを調整します。信号の処理動作が最終プロセスである場合、プロセスは終了し、パス関数は戻りません。信号の処理動作が無視されている場合、プロセスは保留状態になり続け、パスは戻りません。信号の処理動作がキャプチャであれば、信号処理関数を適用した後にpauseは-1に戻り、errnoはENTRに設定されているので、pauseはエラーの戻り値のみとなります。
アクション関数

#include <signal.h> 
int sigaction(int signo, const struct sigaction *act, struct 
sigaction *oact); 
sigaction関数は、指定された信号に関連する処理動作を読み出して修正することができる。調整が成功すれば0に戻り、エラーがあれば-1に戻ります。signoは指定信号の番号です。actポインタ⿪が空であれば、actに基づいてこの信号の処理動作が修正される。oactポインタが空でないと、oactを介してこの信号の元の処理動作が伝えられます。 

int sigemptyset(sigset_t *set); 
関数sigemptysetは、setに向けられた信号セットを初期化し、すべての信号の対応するビットをゼロにし、この信号セットには有効な信号が含まれていない。
二、最適化バージョン
必要な関数の分析

#include <signal.h>
 
int sigsuspend(const sigset_t *sigmask); 
sigsuspendは正常に戻りませんでした。一つの信号処理関数を持ってからsigsuspendだけ戻ります。戻り値は-1で、errnoはENTRに設定されます。sigsuspendを適用すると、プロセスの信号遮蔽はsigmarkパラメータで指定できます。指定されたsigmarkが来た時にある信号に対するシールドを解除して、保留して待ってください。sigsuspendが戻ると、プロセスの信号遮蔽は元の値に戻ります。
sigsuspend関数とpause関数:プログラムを掛けることができますが、sigsuspend関数は信号シールドの解除とマウントを実現できます。
sigprocmark
関数sigprocmarkを使用して、プロセスの信号遮蔽(ブロック信号セット)を読み込みまたは変更できます。

#include <signal.h>
 
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
Osetが⿤空ポインタである場合、読み出しプロセスの現在の信号マスクは、Osetパラメータを介して流れる。setが空のポインタである場合、プロセスの信号マスクを変更します。パラメータhowはどのように変更しますか?Osetとsetが空のポインタである場合、まず元の信号遮蔽をOset⽙にバックアップし、setとhowパラメータに従って信号遮蔽を変更する。
howのオプションの意味

sigprocmarkを適用して、未決の信号に対する現在のブロックが解除されたら、sigprocmarkが戻る前に、その中の一つの信号を少なく送る。
コードの実装:

#include<stdio.h> 
#include<signal.h> 
 
void handler(int signo) 
{} 
 
int mysleep(int timout) 
{ 
  struct sigaction act,oact; 
  sigset_t newmask,oldmask,suspmask; 
  act.sa_handler = handler; 
  act.sa_flags = 0; 
  sigemptyset(&act.sa_mask); 
 
  sigaction(SIGALRM,&act,&oact); 
  sigemptyset(&newmask); 
  sigaddset(&newmask,SIGALRM); 
  sigprocmask(SIG_BLOCK,&newmask,&oldmask); 
 
  alarm(timout); 
 
  suspmask = oldmask; 
  sigdelset(&suspmask,SIGALRM); 
  sigsuspend(&suspmask); 
   
  int unslept = alarm(0); 
  sigaction(SIGALRM,&oact,NULL); 
  sigprocmask(SIG_SETMASK,&oldmask,NULL); 
  return(unslept); 
} 
int main() 
{ 
  while(1) 
  { 
    printf("using musleep!
"); mysleep(3); } return 0; }
最適化バージョンは通常バージョンに存在する競合問題を解決した。普通バージョンのタイミング問題を見直します。
1、SIGALRM信号の処理関数を設定します。
2、アラム関数を呼び出して目覚まし時計をセットする。
3、カーネルの優先度が高いプロセスを選択して、現在のプロセスに取って代わるプロセスが多く、同時に実行時間が長い。
4、アラームがタイムアウトしました。カーネルはこのプロセスにSIGALRM信号を送信し、未決の状態にあります。
5、優先度の高いプロセスが終了したら、カーネルはこのプロセスに戻ります。SIGALRM信号配達、執処理関数sig_alrmの後に再度入園します。
6、このプロセスの主な制御プロセスに戻り、alarm(nsecs)が戻り、pause()を適用して保留する。
7、しかし現在はSIGALRM信号が処理されており、プロセスがエラーを引き起こします。
プロセス実行中に、非同期のために、他の優先度の高いプロセスによって、タイミング問題によるエラー問題が発生する可能性があります。このような問題を競合問題と呼びます。
最適化されたバージョンでは、まずSIGALRM信号の処理関数を設定し、SIGALRM信号をシールドし、次にアラム関数を呼び出してアラームを設定し、その後SIGALRM信号をsigprocmark関数を呼び出してシールドを解除して保留して、競合問題を解決します。
読んでくれてありがとうございます。みなさんのご協力をお願いします。ありがとうございます。