# include <sys/types.h>
# include <signal.h>
# include <pthread.h>
# include <unistd.h>
# include <cstdio>
# include <cstring>
# include <mutex>
# include <thread>
# include <map>
# include <random>
typedef void(*Handler_func)(int);
void sig_handler(int) {
}
int check_signal() {
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGTERM);
sigaddset(&ss, SIGUSR1);
timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
int sig = sigtimedwait(&ss, nullptr, &ts);
if (sig > 0) {
return sig;
} else {
if (errno != EAGAIN) {
char buf[1024];
char *bufp = strerror_r(errno, buf, 1024);
printf("sigtimedwait fail. %s\n", bufp);
return -1;
}
}
return 0;
}
int set_sigprocmask() {
sigset_t newss;
sigemptyset(&newss);
sigaddset(&newss, SIGCHLD);
sigaddset(&newss, SIGUSR1);
sigaddset(&newss, SIGALRM);
sigaddset(&newss, SIGHUP);
sigaddset(&newss, SIGINT);
sigaddset(&newss, SIGQUIT);
sigaddset(&newss, SIGTERM);
if (sigprocmask(SIG_BLOCK, &newss, nullptr)) {
return 1;
} else {
return 0;
}
}
int set_sigaction(int sig, Handler_func func) {
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = func;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, nullptr)) {
return 1;
} else {
return 0;
}
}
int set_sigaction_ign(int sig) {
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, nullptr)) {
return 1;
} else {
return 0;
}
}
int init_signal() {
set_sigaction(SIGCHLD, &sig_handler);
set_sigaction(SIGUSR1, &sig_handler);
set_sigaction(SIGALRM, &sig_handler);
set_sigaction(SIGHUP, &sig_handler);
set_sigaction(SIGINT, &sig_handler);
set_sigaction(SIGQUIT, &sig_handler);
set_sigaction(SIGTERM, &sig_handler);
set_sigaction_ign(SIGPIPE);
if (set_sigprocmask()) {
return 1;
} else {
return 0;
}
}
class th_info {
public:
std::thread th;
pthread_t main_pthread_t;
};
void thread_func(std::mutex &mtx, std::map<int, th_info> &ths, int num) {
// 親の生み出し処理中は止まる
mtx.lock();
mtx.unlock();
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> wait_time(1, 10);
while (!check_signal()) {
sleep(wait_time(mt));
printf("[%d]: send SIGUSR1.\n", num);
mtx.lock();
pthread_kill(ths[num].main_pthread_t, SIGUSR1);
mtx.unlock();
}
}
void main_thread(std::mutex &mtx, std::map<int, th_info> &ths) {
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGHUP);
sigaddset(&ss, SIGTERM);
sigaddset(&ss, SIGINT);
sigaddset(&ss, SIGQUIT);
sigaddset(&ss, SIGUSR1);
while (1) {
int sig;
sigwait(&ss, &sig);
if (sig == SIGUSR1) {
printf("recv SIGUSR1.\n");
} else {
printf("recv FINISH SIGNAL.\n");
break;
}
}
// サブスレッドを回収する
mtx.lock();
for (auto &it : ths) {
pthread_kill(it.second.th.native_handle(), SIGUSR1);
}
mtx.unlock();
for (auto &it : ths) {
it.second.th.join();
}
}
void gen_thread(std::mutex &mtx, std::map<int, th_info> &ths) {
pthread_t main_pthread_t = pthread_self();
mtx.lock();
for (int i = 0; i < 3; ++i) {
std::thread th(thread_func, std::ref(mtx), std::ref(ths), i);
ths[i].th = std::move(th);
ths[i].main_pthread_t = main_pthread_t;
}
mtx.unlock();
}
int main(int, char **) {
if (init_signal()) {
printf("init_signal fail.\n");
return 1;
}
std::mutex mtx;
std::map<int, th_info> ths;
gen_thread(mtx, ths);
main_thread(mtx, ths);
return 0;
}
[root@localhost ~]# g++ --std=c++11 a.cpp -lpthread
[root@localhost ~]#
[root@localhost ~]# ./a.out
[1]: send SIGUSR1.
recv SIGUSR1.
[2]: send SIGUSR1.
recv SIGUSR1.
[0]: send SIGUSR1.
recv SIGUSR1.
[1]: send SIGUSR1.
recv SIGUSR1.
[2]: send SIGUSR1.
recv SIGUSR1.
[2]: send SIGUSR1.
recv SIGUSR1.
[0]: send SIGUSR1.
recv SIGUSR1.
^Crecv FINISH SIGNAL.
[1]: send SIGUSR1.
[2]: send SIGUSR1.
[0]: send SIGUSR1.
[root@localhost ~]#
スレッドを生み出す前に、メインスレッドでpthread_self()を実行して、
それを保存しておけば良い。
サブスレッド側で、メインスレッドに何かを通知したい場合、
pthread_kill(main_pthread_t, SIGUSR1);
という具合に通知できる。
シグナル関連の設定を、あらかじめハンドラをセットした上で
ブロックしておかないと、うまく動かない。
スレッドにした時点で、可能な限りsigwait等を使って、
ハンドラが動作しないようにする。
(なので、プログラム終了時に、サブスレッドが単にsleepで寝ているが、
これらは割り込み処理で抜けることは無く、渡した秒数待ってから終了する)
ひとまず動作するコードにすると、なんだか長くなる・・・。うーん。
2015/12/10 追記
どうにも動作見ていると、メインスレッドに対してシグナルを送ると、
他のスレッドも受信している・・・。
メインスレッドだけがsigwaitでシグナルを捕捉すればよいけど、
他のスレッドもsigwaitで待っていると、
メインスレッドも他のスレッドも、sigwaitから返ってきてしまう・・・。
うーん、サブスレッド -> メインスレッドに送るシグナルを別途決めて、
サブスレッド側で、そのシグナルを待つようなことをしなければ解決だけど。。
もっとスマートに、決めうちでメインスレッドにだけ、シグナルを送れないものかな・・・。
、、、pthread_killでシグナル送るために、メインスレッドのpthread_tを保存したけど、
結局のところ、kill(メインスレッドのpid, SIGUSR1);という処理と同等な模様。
うーん。