マルチプロセスでのスリープ処理

質問:「組込みLinux上でマルチプロセスのアプリケーションを動かします。
あるプロセスで10秒間スリープしたいとき、スレッドを占有しないプログラミングの書き方を教えてください。」
以下はChatGPTの回答。

---

組込みLinux上で**スレッドを占有せずに10秒間スリープ**する方法は、**ノンブロッキングな手法**や**イベント駆動型の仕組み**を使うのが基本です。以下にいくつかのアプローチを紹介します。

---

## ■ 方法1: `select()` を使った非ブロッキング待機

`select()` は本来ファイルディスクリプタの監視に使いますが、**タイムアウトだけを目的に使うこともできます**。

```c
#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()` はカーネル呼び出しなので、**スレッドはカーネルスケジューラで待機状態**になります（実質的に占有しない）。

```c
#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的で、**イベントループ内で時間待ちを処理したい**ときに使う方法。

```c
#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を例に）：

```c
#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方式が決まってたら、それに合わせたコード例も出せるよ！構成を簡単に教えてもらえる？