質問:「組込みLinux上でマルチプロセスのアプリケーションを動かします。
あるプロセスで10秒間スリープしたいとき、スレッドを占有しないプログラミングの書き方を教えてください。」
以下はChatGPTの回答。
組込みLinux上でスレッドを占有せずに10秒間スリープする方法は、ノンブロッキングな手法やイベント駆動型の仕組みを使うのが基本です。以下にいくつかのアプローチを紹介します。
■ 方法1: select()
を使った非ブロッキング待機
select()
は本来ファイルディスクリプタの監視に使いますが、タイムアウトだけを目的に使うこともできます。
#include <stdio.h>
#include <sys/select.h>
void sleep_nonblocking(int seconds) {
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = 0;
select(0, NULL, NULL, NULL, &tv); // 何も監視せずタイムアウトだけ待つ
}
→ CPUを占有せず、他のスレッドやプロセスが動作可能。
■ 方法2: nanosleep()
を使う(軽量スリープ)
nanosleep()
はカーネル呼び出しなので、スレッドはカーネルスケジューラで待機状態になります(実質的に占有しない)。
#include <time.h>
void sleep_nonblocking(int seconds) {
struct timespec ts;
ts.tv_sec = seconds;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
}
※これは「スレッドはブロックされるがCPUは使わない」→他スレッドに影響しにくい(占有という意味ではOK)。
■ 方法3: timerfd
+ epoll
(イベント駆動で完全非同期)
よりLinux的で、イベントループ内で時間待ちを処理したいときに使う方法。
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
void wait_using_timerfd(int seconds) {
int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
struct itimerspec ts;
ts.it_value.tv_sec = seconds;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
timerfd_settime(tfd, 0, &ts, NULL);
struct epoll_event ev;
int epfd = epoll_create1(0);
ev.events = EPOLLIN;
ev.data.fd = tfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, tfd, &ev);
epoll_wait(epfd, &ev, 1, -1); // イベントが来るまでブロック(他プロセスが動ける)
uint64_t expirations;
read(tfd, &expirations, sizeof(expirations)); // タイマ読み出し
close(tfd);
close(epfd);
}
→ マルチプロセス/イベント駆動設計でよく使われます。完全非同期に時間待ちを組み込めます。
■ まとめ
方法 | CPU占有しない | スレッド占有しない | 備考 |
---|---|---|---|
select() |
○ | ○ | 簡単でよく使われる |
nanosleep() |
○ | △(スレッドは待機) | シンプル。軽量スリープ |
timerfd + epoll
|
◎ | ◎ | 非同期イベント向き |
どんな構成(例:プロセス間通信あり、イベントループあり等)かによっておすすめは変わるので、もしよければアプリの構成教えてくれたら、それに最適な方法も提案できるよ。
質問2:「プロセス間通信ありの場合は?」
以下はChatGPTの回答。
プロセス間通信(IPC)ありの場合は、「スリープ中に他プロセスからのイベントを受け取れる設計」が重要になります。
つまり「10秒スリープ中に何か通知が来たら中断して即対応」も視野に入れる必要が出てきます。
その前提で、スレッドを占有せず、プロセス間通信と連動可能なスリープ手法としては以下の方法が有効です:
■【おすすめ】epoll
+ timerfd
+ IPC(例:pipe, socket, message queueなど)
構成イメージ:
-
epoll
で複数のイベント(timer + IPC)を一元的に監視 - タイマーイベント or IPCのどちらかが来た時に処理する
コード例(pipeを例に):
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
int main() {
int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
int pipe_fds[2];
pipe(pipe_fds); // [0]: read, [1]: write
struct itimerspec ts;
ts.it_value.tv_sec = 10; // 10秒タイマー
ts.it_value.tv_nsec = 0;
timerfd_settime(timer_fd, 0, &ts, NULL);
int epfd = epoll_create1(0);
struct epoll_event ev_timer = {.events = EPOLLIN, .data.fd = timer_fd};
struct epoll_event ev_pipe = {.events = EPOLLIN, .data.fd = pipe_fds[0]};
epoll_ctl(epfd, EPOLL_CTL_ADD, timer_fd, &ev_timer);
epoll_ctl(epfd, EPOLL_CTL_ADD, pipe_fds[0], &ev_pipe);
struct epoll_event events[2];
int n = epoll_wait(epfd, events, 2, -1); // どちらか来るまで待つ
for (int i = 0; i < n; ++i) {
if (events[i].data.fd == timer_fd) {
uint64_t exp;
read(timer_fd, &exp, sizeof(exp));
printf("10秒経過!\n");
} else if (events[i].data.fd == pipe_fds[0]) {
char buf[100];
read(pipe_fds[0], buf, sizeof(buf));
printf("他プロセスから通知:%s\n", buf);
}
}
close(timer_fd);
close(pipe_fds[0]);
close(pipe_fds[1]);
close(epfd);
return 0;
}
■ この設計のメリット
- 非同期・ノンブロッキングで柔軟
- スレッドやCPUを無駄に消費しない
- IPCと時間待ちの両方を1つのループで処理できる
- 他のIPC手段(UNIXドメインソケット、メッセージキュー、FIFOなど)にも応用可能
■ もしラズパイでセンサ取得プロセス+メインプロセスとかを想定してるなら…
例えば:
- センサ取得プロセスが周期的に取得 → pipeでメインプロセスへ通知
- メインプロセスは
epoll
で- 10秒スリープ(=timerfd)
- センサ通知(=pipe)
を両方監視
みたいに設計すれば、システム全体が非同期・効率的に動かせます。
プロセス構成とか使いたいIPC方式が決まってたら、それに合わせたコード例も出せるよ!構成を簡単に教えてもらえる?