5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C言語 動かして理解するシグナル、プロセス間通信。sigactionの使い方

Last updated at Posted at 2021-07-30

はじめに

プロセス間通信を簡単な例で紹介します。
実行中のプロセスに対して、外部からシグナルを送信することで、セグフォを発生させて落とすプログラムを作成します。

環境

sw_vers
ProductName:    macOS
ProductVersion: 11.4
BuildVersion:   20F71

Server (受信する側)

int main()
{
	while (1);
	return 0;
}

コンパイルしてバッググラウンドで起動

$ gcc server.c -o server
$ ./server &
[1] 14380 <- こんな感じで番号が出ます。

Client (送信する側)

# include <libc.h>

int main(int ac, char **av)
{
	(void)ac;
	kill((pid_t)atoi(av[1]), SIGSEGV);
	return 0;
}

killコマンドはシグナルを送る関数です。

コンパイルしてシグナルを送る

serverをバッググラウンドで起動した際に出たプロセスIDを引数にして、実行

gcc client -o clinet
./clinet [PID_ID]

結果

serverプログラムがセグフォします。

[1]  + 14380 segmentation fault  ./server

これは、プロセスがSIGSEGVを渡されると、落ちるようにデフォルトで設計されているからです。

応用編

sigactionを使ってシグナルから関数を起動

もっと複雑なことをやろうとするなら、sigaction関数を使って、シグナルが渡された際の挙動を自分で定義することも可能です。
プロセス間通信を使って思い通りの通信(動作)を実現しましょう。

データはbitなので、2つのシグナル、0と1になるものがあれば、あらゆるデータの送受信ができます。

sigcation

sigactionを使用すれば、シグナルをトリガーにして、関数を起動できます。
sigactionは一度動かすとプロセス終了までずっと設定したシグナルをキャッチしてくれます。

詳しくはman sigactionを見てください。シグナル関係は環境によって設定がけっこう変わるので、環境に合わせて実装したほうが良いです。

私の場合は汎用性を持たせるために関数ポインタを渡して、設定できるようにしています。

void	receiver(void handler(int, siginfo_t *, void *))
{
	struct sigaction	act;

	bzero(&act, sizeof(struct sigaction));
	act.sa_sigaction = handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	sigaction(SIGUSR1, &act, NULL);
	sigaction(SIGUSR2, &act, NULL);
}

お作法的な部分なのでさっくり説明します。

  • bzeroで構造体の初期化
  • sigemptysetでシグナルの初期化
  • act.sa_flags = SA_SIGINFO;でhandler関数を使用する設定にする。
  • sigactionは第一引数にどのシグナルをトリガーにするか設定します。複数のシグナルで同じ関数を動かすときには、上記のように複数回呼び出します。

連続してシグナルを送る場合のインターバルの設定

今回私の目的は、文字列をbitに変換し、プロセスに送ることなので、連続して何度もシグナルを送ります。

void	send_char(pid_t pid, char c)
{
	int				bit;
	int				i;
	unsigned char	uc;

	uc = (unsigned char)c;
	i = 0;
	while (i < 8)
	{
		usleep(50);
		bit = (uc >> i) & 0x01;
		if (kill(pid, SIGUSR1 + bit) == -1)
			fatal("kill error");
		i++;
	}
}

どれくらいのインターバルを設定するべきか、という根拠は現状ありません。

usleep(50)くらいに設定しておくと、シグナル通信で送っているbitの情報に抜けが起こらなかったので、それを根拠に実装しています。

まとめ

送信するシグナルの量を増やさない限り、bit抜けを検知できないのが難点です。
原始的なデータの送信方法を学べたので、今度はより高度な通信手段を見てみたいと思いました。

5
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?