コンテンツ
I.はじめに
コードセグメント、データセグメント、スタックセグメントは、fork()によって開かれた子プロセスと親プロセスの間で共有されますが、親プロセスと子プロセスの間のデータは共有されません。つまり、データ送信は実行できません。プロセス間のIPC通信には2つの方法があります。1つはパラメーターとバインドするシグナルsigaction()であり、シグナルはsigqueue()を介して送信されます。もう1つは、引数なしのシグナルsignal()バインディングで、 kill()を介してシグナルを送信します。
パラメータを使用すると、信号送信でintタイプのデータを送信することもできます。つまり、親プロセスと子プロセス間のデータ転送を実現できます。パラメータのある信号はint型のデータを渡すことができますが、パラメータのない信号はデータを渡すことができません。この記事では、パラメーターを使用したシグナルsigaction()バインディングと、2つのプロセス間でint型データが渡されることを確認するためのsigqueue()シグナル送信について紹介します。
この記事では、 sigqueue()を介してシグナルを送信するため のパラメーターとのシグナルsigaction()バインディングについて説明します。
2、sigaction()ライブラリ関数の紹介
1. signation()関数のマニュアルを確認します
Linuxのターミナルにmansigactionと入力して、関数プロトタイプと関連する関数の紹介を表示します。
2. sigaction()関数プロトタイプ
sigaction()関数は、特定のシグナルを受信した後のプロセスの動作を変更するために使用されます。
ヘッドファイル:
#include <signal.h>
関数プロトタイプ:
int sigaction(int signum、const struct sigaction * act、struct sigaction * oldact);
パラメータ:
signum:sigkillとsigstopを除く特定の有効な信号
行為:構造体sigaction構造体ポインター
oldact:NULLとして指定できます
戻り値:
この関数は、成功した場合は0を返し、失敗した場合は-1を返します。
3.構造体sigaction構造
構造体の最初の2つのデータ型は関数ポインターであり、関数ではなく、構造体の構造体を破壊しません。sa_flagsはフラグビットです。sa_flags= SA_SIGINFOの場合、信号はパラメータ付きで指定され、sa_flags = 0の場合、信号はパラメータなしで指定されます。
構造プロトタイプ:
struct sigaction { void(* sa_handler)(int); //関数ポインタ void(* sa_sigaction)(int、siginfo_t *、void *);//関数ポインタ sigset_tsa_mask;//信号セット intsa_flags;//フラグビット void (* sa_restorer)(void); };
3、sigqueue()関数の紹介
1.機能
sigqueue()関数は、信号を送信するための新しいシステムコールであり、主に、パラメーターを使用してリアルタイム信号用に提案され、関数sigaction()と組み合わせて使用されるサポート信号用です。
sigaction():シグナルを関数にバインドします。
sigqueue():パラメータを使用してシグナルを送信した後、シグナルバインディングを実行する関数。
2.関数プロトタイプ
ヘッドファイル:
#include <signal.h>
関数プロトタイプ:
int sigqueue(pid_t pid、int sig、const union sigval value);
パラメータ:
pid:シグナルを受信するプロセスIDを指定します
sig:送信されたシグナル
値:unionデータ構造union sigval、シグナルによって渡されるパラメーターを指定します
戻り値:
成功した場合は0を返し、失敗した場合は-1を返します
3.ユニオンデータ構造ユニオンシグバル
union sigval unionデータ構造は、信号伝送のパラメーターを指定します。つまり、sival_intを介してintタイプのデータを保存し、sigqueue関数を介してintタイプのデータを渡します。
プロセス間データ転送は可能ですが、転送できるのはint型データのみです。sival_ptrの機能が開発されていないため、このデータ転送はまだ比較的味がありません。
構造プロトタイプ:
union sigval { intsival_int;//データ転送用にint型のデータを保存します void*sival_ptr; //さまざまな型のポインタを保存しますが、このメソッドは開発されていません };
第四に、プロセス間のデータ転送テスト
1.バインディング用の関数をカプセル化します
まず、sigaction()の関数をカプセル化してシグナルをバインドします。
void signal_function(int signum, siginfo_t* info, void*);
void signal_function(int signum, siginfo_t* info, void*)
{
int x = info->si_int;
cout << "pid = " << getpid() << " 函数被调用了 signum = " << signum << "传递的信息info = " << x << endl;
}
2.使用されるヘッダーファイル
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys / types.h>
#include <sys / wait.h>
3.主な機能
親プロセスによって送信されたシグナルのパラメーターが整数データ12345を保存するかどうか、および子プロセスがこのデータを受信できるかどうかをテストします。
int main()
{
//带参数的信号
int pid = 0;
int status = 0;
struct sigaction act;
act.sa_sigaction = signal_function;//保存函数指针
act.sa_flags = SA_SIGINFO;//标志位,指定信号是带参数的
//带参数的信号绑定
sigaction(SIGRTMIN, &act, NULL);
pid = fork();
if (pid == 0)//子进程
{
while (1)
{
cout << "子进程运行中 pid = " << getpid() << endl;
sleep(1);
}
}
else if (pid > 0)//父进程
{
sleep(3);//先让子进程先处理业务,测试sigqueue函数
union sigval value;//联合结构体
value.sival_int = 12345; //保存传递的int类型的数据
for (int i = 1; i <= 5; i++)
{
cout << "父进程给子进程发送第 " << i << " 个信号" << endl;
//带参数发送信号
sigqueue(pid, SIGRTMIN, value);
}
sleep(3);//延时3秒
kill(pid, SIGKILL);//给子进程发送停止信号
waitpid(pid, &status, 0);//等待子进程结束,防止子进程托孤
cout << "子进程结束" << endl;
}
return 0;
}
結果は次のとおりです。
子プロセスが配置されている関数は、親プロセスによって送信されたパラメータを含む信号を受信し、パラメータによって保存されたデータ12345も子プロセスに渡されます。つまり、親プロセス間のデータ転送です。子プロセスは成功します。
5、信号シールド
プロセスがシグナルを受信すると、そのシグナルによってプロセスが中断されます。プロセスがシグナルを認識すると、バインドされた機能を実行します。シグナルを認識しない場合、プロセスは終了します。
それで、それを回避する方法は?このとき、信号セットに格納されている信号をシールドするためにsigset_t(信号セット)が必要です。これにより、ランダム信号によってプロセスが終了することはありませんが、ランダム信号がシールド信号セットに含まれていることが前提となります。 。
信号セット操作機能
働き | 効果 |
int sigemptyset(sigset_t * set); | 明確な信号セット |
int sigfillset(sigset_t * set); | すべての信号を追加します |
int sigaddset(sigset_t * set、int signo); | 信号を増やす |
int sigdelset(sigset_t * set、int signo); | 信号セットから信号を削除します |
int sigismember(const sigset_t * set、int signo); | 信号がセットに含まれているかどうか |
sigprocmask()関数
関数プロトタイプ:
int sigprocmask(int how、const sigset_t * set、sigset_t * oldset);
パラメータ:
どのように意味するか:
SIG_BLOCK setには、現在の信号マスクに追加する信号が含まれています。これは、mask = mask | set(ビットOR演算)と同等です。
SIG_UNBLOCKセットには、現在の信号マスクからブロックを解除する信号が含まれています。これは、mask = mask ^ set(ビットXOR演算)と同等です。
SIG_SETMASK現在のシグナルマスクワードをsetが指す値に設定します。これはmask=setと同等です。
セットとオールドセット:
oldsetがnull以外のポインターである場合、読み取りプロセスの現在のシグナルマスキングステータスワードは、oldsetパラメーターを介して渡されます。
setがnull以外のポインタの場合、プロセスのシグナルマスクステータスワードが変更され、パラメータhowは変更方法です。
oldsetとsetの両方がnull以外のポインタである場合は、最初に元の信号マスクワードをoldsetにバックアップしてから、setとhowパラメータに従って信号マスクワードを変更します。
戻り値:
成功した場合は0を返し、エラーの場合は-1を返します
コードテスト
コードの一部を介して信号シールドをテストしてみましょう
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int main()
{
//准备两个信号集()
sigset_t arry;//存放需要屏蔽的信号
sigset_t temp_arry;//存放未决信号
//信号集初始化
sigemptyset(&arry);
sigemptyset(&temp_arry);
//添加屏蔽的信号
sigaddset(&arry, SIGUSR1);//屏蔽第10个信号
if (sigprocmask(SIG_BLOCK, &arry, NULL) < 0) //更改进程的信号屏蔽字
{
perror("signal error");
exit(0);
}
while(1)
{
cout << "程序运行中 pid = " << getpid() << endl;
sleep(3);
sigpending(&temp_arry);//读取当前进程的未决信号集
if (sigismember(&temp_arry, SIGUSR1)) //如果未决信号集里有第10个信号
{
cout << "有未决信号进来 SIGUSER1 发过来了" << endl;
sigdelset(&temp_arry, SIGUSR1);
}
}
}
Visual Studioメニューバーをクリックします:ビルド->ソリューションの再構築。現時点では、Windowsでコードに加えられた変更は、Linuxでも同期されます。
Linuxでプロジェクトディレクトリが配置されているフォルダを見つけ、右クリックしてターミナルを開き、次のように入力します。g ++ main.cpp -o main
コンパイルが成功したら、次のように入力します:./mainそして実行します
別の端末でkillを介してメインプロセスにシグナルを送信します
マスクされた信号No.10が過去に送信され、保留中の結果セットに入ることがわかります。これはプロセスに影響を与えません。また、マスクされていない信号No. 13は過去に送信されたものであり、プロセスはこの信号を認識せず、直接処理を終了します。
同時に、プロセスが未知の信号を受信した場合、プロセスは何をすべきかを知らず(関連する関数がバインドされていないため)、プロセスが直接終了することも確認します。
6.信号の競合
プロセスが2つのシグナルを認識したとします(両方のシグナルは関連する関数にバインドされます)。最初のシグナルが送信されると、プロセスはシグナルにバインドされた関数ビジネスを実行し、ビジネスは完了せず、2番目のシグナルが送信されます。 。このとき、第1の信号処理の機能業務が中断され、第2の信号バインディングの機能業務に移行します。このとき、信号の競合が発生します。
それで、それをどのように解決するのですか?sigaction()を介してシグナルをバインドし、sigaddset()を介してシールドシグナルを長時間ビジネスを処理する関数に追加できます。コードでテストしてみましょう。
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void test1(int i);
void test2(int i);
int main()
{
struct sigaction act1;
struct sigaction act2;
//保存函数指针
act1.sa_handler = test1;
act2.sa_handler = test2;
//指定信号不带参数
act1.sa_flags = 0;
act2.sa_flags = 0;
//信号绑定函数
sigaction(SIGUSR1, &act1, NULL);
sigaction(SIGUSR2, &act2, NULL);
//初始化信号集
sigemptyset(&(act1.sa_mask));
//屏蔽信号SIGUSR2
sigaddset(&(act1.sa_mask), SIGUSR2);
while (1)
{
cout << "进程运行中 pid = " << getpid() << endl;
sleep(2);
}
return 0;
}
void test1(int i)
{
cout << "函数 test1 被调用了 i = " << i << endl;
sleep(10);//延时10秒
cout << "函数 test1 结束了" << endl;
}
void test2(int i)
{
cout << "函数 test2 被调用了 i = " << i << endl;
}
Linuxのターミナルに上記と同じものを入力します:g ++ main.cpp -o main
コンパイルが成功したら、次のように入力します:./mainそして実行します
10番信号(SIGUSER1)が送信された後、すぐに12番信号(SIGUSER2)が送信され、関数test1()は中断されませんが、関数test2()は関数test1の後に呼び出されます。 ()が終了します。つまり、sigaddset(&(act1.sa_mask)、SIGUSR2)このコードは、信号の競合の問題を解決します。
次に、コードsigaddset(&(act1.sa_mask)、SIGUSR2)をコメントアウトして、再度テストします。
関数test1()が終了する前に関数test2()が呼び出されている、つまり信号の競合が発生し、関数test1()が中断されていることがわかります。
注:
新しいシグナルと処理中のシグナルが同じタイプの場合、それらはキューに入れられ、前のシグナルによって実行されたビジネスを中断しません。
新しい信号と処理中の信号は異種信号であり、前の信号によって実行されたビジネスを中断します。
7.まとめ
シグナルの利点
1.プロセスおよびプロセス間は通知の役割を果たすことができます。
2.データ転送が可能です。
信号のデメリット
1.型intのデータのみを渡すことができます。
2.プロセスが未知の信号を受信すると、プロセスの実行を中断します。これは、信号シールドによって解決する必要があります。
3.プロセスがシグナルフォローアップ操作を処理していて、新しいシグナルが完了していない場合、シグナルの競合の問題が発生します。
オリジナリティは簡単ではありません。転載の際は出典をお知らせください。
それがあなたに役立つなら、あなたは好きで、集めて+フォローすることができます、そしてそれは継続的に更新されます(hee hee)。