前の記事に続き、この章ではまず、sigqueueやsigactionなどのパラメーターと関数を使用したシグナルについて学習します。
VS2019 C ++(1.4)に基づくクロスプラットフォーム(Linux)開発-Signal
I.はじめに
前回の記事で説明したkill関数は、特定のプロセスに対してのみシグナルを送信し、パラメーターを反映しません。また、プロセス内でデータ共有を行うことはできません。子プロセス間でグローバル変数を定義して変更しても、親プロセスは変更されているかどうかわかりません。したがって、この状況を解決するにはIPC通信が必要であり、killはデータを送信できないため、パラメーターを含む信号を使用する必要があります。
2.関数プロトタイプ
1.sigactionライブラリ関数-バインディングシグナル
機能:特定の信号を受信した後、プロセスの動作を変更するために使用されます。
ヘッダーファイル<signal.h>
原型:int sigaction(int signum、const struct sigaction * act、const struct sigaction * old);
パラメータ
- 最初のパラメーターはシグナルの値です。これは、sigkillとsigstopを除く特定の有効なシグナルにすることができます(これら2つのシグナルに対して独自の処理関数を定義すると、シグナルのインストールエラーが発生します)。
- 2番目のパラメーターは、構造体sigactionのインスタンスへのポインターです。構造体sigactionのインスタンスでは、特定のシグナルの処理が指定されています。空にすることもでき、プロセスはデフォルトでシグナルを処理します。
- 3番目のパラメーターoldactが指すオブジェクトは、対応する信号(通常はnull )の元の処理を保存するために使用されます。
注:パラメーター2および3は、構造体へのポインターです。2番目のパラメータが最も重要です。この構造には信号処理機能が含まれていますが、この機能を使用するときに2番目のパラメータに直接割り当てることはできません。
戻り値:この関数は、成功した場合は0を返し、失敗した場合は-1を返します。
Linuxでsigaction関数を次のように表示します
対応する2番目のパラメータsigactionの構造は次のとおりです。
c構造体に関数を含めることはできませんが、ここでは関数ポインターのみが予約されており、構造体に関数を含めることができないという規定に違反することはありません。構造体の場合、これは単なるポインター変数です(より特別な、関数を指す)
最初の関数は、上記のパラメーターのない関数ポインターに似ているため、2番目の関数を使用してパラメーターを使用して信号を実装し、sa_flagsを使用してパラメーターがあるかどうかを判別します。
2、sigqueueライブラリ関数-シグナルの送信
関数:主にリアルタイム信号の信号を送信するための新しいシステムコールは、パラメーターを持つ信号をサポートし、関数sigaction()とペアで使用されます。
原型:int sigqueue(pid_t pid、int sig、const union sigval value);
パラメータ
- 最初のパラメーターは、受信信号を指定するプロセスIDです。
- 2番目のパラメーターは、送信されようとしている信号を決定します
- 3番目のパラメーターはuniondatastructure union sigval(構造体に似ていますが、ほとんど使用されません)で、シグナルによって渡されるパラメーター、つまりいわゆる4バイト値を指定します。
戻り値:成功の場合は0、失敗の場合は-1
Linuxでsigqueue関数を次のように表示します
2番目のパラメーターの対応するsigvalユニオンは次のとおりです。
ここで、sigval_intは送信される整数データです。
3.実現
1.サンプルコード
この例では、親プロセスを実装してシグナルを送信し、データを子プロセスに渡します
①以前のvoidsignalFunc(int i)は、パラメータなしの信号の使用にのみ適しているため、再定義された関数は次のようになります。
void sigaction_func(int num、siginfo_t * info、void * vo)
最初のパラメータnumは受信信号コードであり、受信データは2番目のパラメータ情報に保存されます。
②sigqueueによって送信されるデータは、コンソーシアムの形式である必要があります
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include<iostream>
#include <signal.h>
#include<stdio.h>
#include <stdlib.h>
using namespace std;
//带参数的信号
void sigaction_func(int num, siginfo_t* info, void* vo);
int main(){
int pid = 0;
//准备结构体
struct sigaction act;
act.sa_sigaction = sigaction_func;
act.sa_flags = SA_SIGINFO;//带参数的信号
//绑定信号
sigaction(SIGUSR1,&act,NULL); //发送是sigqueue
pid = fork();
if (pid == 0)
{
while (true)
{
cout << "子进程运行中 ...pid =" << getpid() << endl;
sleep(1);
}
}
else if (pid > 0)
{
sleep(2);
//准备要传递的数据
union sigval value;
value.sival_int = 111;
//带参数发送信号
sigqueue(pid, SIGUSR1, value);//第三个参数跟之前不一样,是一个联合体
//卡住父进程
while (1) {}
}
return 0;
}
void sigaction_func(int num, siginfo_t* info, void* vo)
{
int x = info->si_int;
cout <<"当前进程pid = "<<getpid()<<" sigaction_func函数被调用了,num = " << num << " 信号传递过来的数据是 x = " << x<< endl;
}
2.実行結果
4.まとめ
前の例で渡されたデータは単なる整数であるため、他のタイプのデータを実装するために使用できると考えて、sigvalユニオンの2番目のパラメーターvoid *に気付く人もいますが、これは場合。
1.初期の開発者がシグナルを開発したとき、彼らはint型のデータ転送のみをサポートしていたため、後で使用するためにvoid *を予約することを考えましたが、残念ながら、void*のデータ転送はubuntu20バージョンではまだサポートされていません。まだ実行されていません。更新のため、複雑なデータ(配列、構造)を渡すことができず、非常に味がありません。
2. IPCには5つの通信方法があるため、信号を使用してプロセス間でデータを転送できるだけでなく、信号の操作が面倒であるため、通信の基本的な用途しかありません。
3.子プロセスが不明なシグナルを受信し(たとえば、上記のコードのSIGUSR1をsigqueue(pid、SIGUSR1、value);のSIGUSR2に変更)、バインディング関数が実行されない場合、実行結果は次のようになります。子プロセスは何もしないと行き詰まります
Linuxで再度コンパイルする
この時点で、プロセス状態がZ +(ゾンビ状態-子プロセスは親プロセスの前に終了します。子プロセスには無限ループがあり、最初に終了するべきではありませんが、子プロセスが不明な信号を受信するため、それは中断され、子プロセスはそこでスタックします。つまり、元のプロセスは実行されず、次に何をすべきかわからず、ゾンビ状態になり、リソースを占有するため、回避する必要があります)
4.プロセスが1つしかない場合は、killを使用してLinuxで彼にシグナルを送信します
プロセスは不明な信号(信号番号10)を受信し、ユーザー定義の信号1を出力し、プロセスのステータスを確認し、pidが見つからないこと、つまり、pidを再生したかのように中断されて破棄されていることを検出します。プロセスを強制終了する役割(前の親プロセスは不明なシグナルを子プロセスに送信しますが、スタックします)。したがって、プロセスを正常に実行するには、信号シールドを使用する必要があります
小規模チョッパー
ロード時にソースを示してください
VS2019 C ++(1.4.2)に基づくクロスプラットフォーム(Linux)開発-パラメーター信号を使用
学習信号マスクは次のとおりです。