OS・実行環境

POSIX

UNIX系OS共通のAPI規格。移植性を高める。

概要

POSIX(ポジックス:Portable Operating System Interface)は、UNIX系オペレーティングシステムが提供すべきAPIを定めたIEEE規格です。正式名称はIEEE Std 1003で、Single UNIX Specification(SUS)とも統合されています。POSIXに準拠したOSはAPIの互換性があり、同じソースコードを異なるOS(Linux・macOS・FreeBSD・QNX等)向けにコンパイルして動作させる「移植性(ポータビリティ)」が確保されます。

組み込み分野では、組み込みLinuxはPOSIX準拠のOSとして、ファイルI/O・プロセス管理・スレッド・ネットワーク・IPC(プロセス間通信)などのAPIを提供します。RTOSでもPOSIX準拠(またはサブセット準拠)を謳う製品が多く(VxWorks・QNX等)、アプリケーションの移植性向上に寄与しています。

歴史・背景

1980年代、UNIX系OSはAT&T Unix・BSD Unix・Xenix等、多数の派生版が乱立し、OSごとにAPIが微妙に異なるという問題がありました。このAPIの非互換性を解消するため、IEEE(米国電気電子学会)が主導して1988年に「POSIX.1(IEEE Std 1003.1-1988)」が策定されました。名前は著名なハッカーRichard Stallmanが提案した造語です。

1990年代にPOSIX.1(基本API)・POSIX.2(シェル・ユーティリティ)・POSIX.4(リアルタイム拡張)・POSIX.1b(POSIX Realtime Extensions)など、複数の規格が策定されました。2001年にはIEEE・The Open Group・ISO/IECが協力して統合版「Single UNIX Specification Version 3(SUSv3 / POSIX.1-2001)」を発行しました。

最新版はPOSIX.1-2024(IEEE Std 1003.1-2024)で、スレッド・ソケット・タイマー・シグナル等の現代的なAPIが標準化されています。LinuxはPOSIX準拠を目標として開発されていますが、完全準拠ではなくLinux固有の拡張が多数あります。

技術仕様

POSIX APIのカテゴリ

POSIX.1 主要API カテゴリ:

1. ファイルとディレクトリ操作
   open(), read(), write(), close(), lseek()
   stat(), mkdir(), rmdir(), link(), unlink()
   opendir(), readdir(), closedir()

2. プロセス管理
   fork(), exec(), wait(), exit(), getpid()
   signal(), kill(), sigaction()

3. メモリ管理
   mmap(), munmap(), mprotect()
   malloc() (C標準ライブラリ経由)

4. ファイルロック・I/O
   fcntl(), ioctl()
   select(), poll(), epoll()(Linuxはepoll追加)

5. POSIX スレッド(pthread)
   pthread_create(), pthread_join(), pthread_exit()
   pthread_mutex_lock/unlock()
   pthread_cond_wait/signal()
   pthread_rwlock_*()
   sem_init(), sem_wait(), sem_post()

6. プロセス間通信(IPC)
   pipe(), mkfifo()
   shm_open(), shm_unlink()(POSIX共有メモリ)
   mq_open(), mq_send(), mq_receive()(POSIXメッセージキュー)
   sem_open()(POSIXセマフォ(名前付き))

7. ネットワーク(Sockets API, POSIX.1g)
   socket(), bind(), listen(), accept(), connect()
   send(), recv(), sendto(), recvfrom()
   getaddrinfo(), getnameinfo()

8. タイマー(POSIX Realtime)
   clock_gettime(), clock_settime()
   timer_create(), timer_settime()
   nanosleep()

POSIXスレッド(pthreads)の例

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;
int data_ready = 0;

void *producer(void *arg) {
    for (int i = 0; i < 10; i++) {
        /* データ生成 */
        pthread_mutex_lock(&mutex);
        data_ready = 1;
        pthread_cond_signal(&cond);   /* 消費者に通知 */
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void *consumer(void *arg) {
    pthread_mutex_lock(&mutex);
    while (!data_ready) {
        pthread_cond_wait(&cond, &mutex); /* データを待つ(ミューテックスを解放して待機) */
    }
    /* データ処理 */
    data_ready = 0;
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main(void) {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, producer, NULL);
    pthread_create(&t2, NULL, consumer, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}

POSIXリアルタイム拡張(POSIX.1b)

POSIX.1bはリアルタイム機能の標準化を行っています:

#include <time.h>
#include <sched.h>

/* 高精度タイマーで現在時刻取得 */
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); /* モノトニック時計(単調増加)*/
/* ts.tv_sec:秒、ts.tv_nsec:ナノ秒 */

/* スレッドのリアルタイム優先度設定 */
struct sched_param param;
param.sched_priority = 80;  /* 優先度1〜99(高いほど優先) */
pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);

/* ナノ秒精度スリープ */
struct timespec sleep_time = {0, 500000};  /* 500µs */
nanosleep(&sleep_time, NULL);

/* POSIX タイマー(シグナルで通知) */
timer_t timerid;
struct sigevent sev = {.sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGUSR1};
timer_create(CLOCK_MONOTONIC, &sev, &timerid);
struct itimerspec its = {
    .it_interval = {0, 1000000},  /* 1ms周期 */
    .it_value    = {0, 1000000}
};
timer_settime(timerid, 0, &its, NULL);

select / poll / epoll による非同期I/O

/* select()によるI/O多重化(POSIX準拠) */
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(uart_fd, &rfds);
FD_SET(spi_fd, &rfds);

struct timeval tv = {1, 0};  /* 1秒タイムアウト */
int ret = select(max_fd + 1, &rfds, NULL, NULL, &tv);
if (ret > 0) {
    if (FD_ISSET(uart_fd, &rfds)) { /* UARTにデータあり */ }
    if (FD_ISSET(spi_fd,  &rfds)) { /* SPIにデータあり */ }
}

/* epoll()(Linux固有・高性能) */
int epfd = epoll_create1(0);
struct epoll_event ev = {.events = EPOLLIN, .data.fd = uart_fd};
epoll_ctl(epfd, EPOLL_CTL_ADD, uart_fd, &ev);
struct epoll_event events[10];
int n = epoll_wait(epfd, events, 10, 1000);  /* 1秒タイムアウト */

動作原理

POSIXとシステムコールの関係

POSIXのAPIはC標準ライブラリ(glibc・musl)で実装され、その内部でLinuxのシステムコールを呼び出します:

ユーザーコード:  pthread_mutex_lock(&mutex);

glibc:          futex(uaddr, FUTEX_WAIT, ...)  ← システムコール発行

Linuxカーネル:  futexサブシステムでスレッドをスリープ

(ロック解放時)
Linuxカーネル:  FUTEX_WAKEで待機スレッドを起こす

ユーザーコード: pthread_mutex_lock() が返る

POSIXのRTOS実装

VxWorks・QNX・μC/OS-IIはPOSIX準拠(または部分準拠)を提供します。FreeRTOSはデフォルトではPOSIX非準拠ですが、POSIX互換ラッパー(FreeRTOS-Plus-POSIX)が提供されており、pthread_create()等の関数が利用できます:

/* FreeRTOS-Plus-POSIX での pthread 使用例 */
#include <FreeRTOS_POSIX/pthread.h>

pthread_t thread;
pthread_create(&thread, NULL, my_function, NULL);
pthread_join(thread, NULL);

用途・ユースケース

組み込みLinuxアプリケーションの移植性確保

アプリケーションをPOSIX APIのみで実装することで、Linuxディストリビューション間の移植性や、将来別のPOSIX準拠OSへの移行が容易になります。

RTOSへの移植

POSIX APIを使って書いたアプリは、POSIX準拠RTOSへの移植が比較的容易です。QNXはPOSIX準拠度が高く、LinuxアプリをほぼそのままQNXに移植できる場合があります。

CI/CDでのクロスプラットフォームテスト

組み込みLinux向けのビジネスロジックをPOSIX APIで実装し、x86のLinux PC(macOS含む)でユニットテストを行うことで、CI/CD環境での自動テストが可能になります。

実装・開発のポイント

よくある移植性の問題

/* POSIXの落とし穴:定数のサイズが実装依存 */
int a;            /* サイズ不定(移植性に問題) */
int32_t b;        /* <stdint.h> の固定幅型を使う(推奨) */

/* Linux固有APIはPOSIX非準拠(macOS等では使えない場合あり) */
epoll_create1(0);          /* Linux固有(POSIX非準拠) */
inotify_init();            /* Linux固有 */
timerfd_create();          /* Linux固有 */
signalfd();                /* Linux固有 */
/* 代替:select()/poll() は POSIX 準拠 */

/* pthreadのエラー処理(errno非使用) */
int ret = pthread_create(&t, NULL, fn, NULL);
if (ret != 0) { /* 0以外がエラーコード(errnoではない) */ }

POSIX準拠の確認

# コンパイル時のPOSIX機能マクロ
/* 特定POSIX版の機能を有効にする */
#define _POSIX_C_SOURCE 200809L   /* POSIX.1-2008 */
#define _XOPEN_SOURCE 700         /* XSI拡張含む */

他技術との比較

POSIX vs Windows API

WindowsはPOSIXとは異なるAPI(Win32 API)を持ちます。MinGW・Cygwin・WSL(Windows Subsystem for Linux)はWindowsでPOSIXを使えるようにする互換レイヤーですが、完全互換ではありません。組み込みLinuxアプリはPOSIX APIを使っていれば基本的にWindowsでのビルドは不要で、クロスコンパイル環境でLinux向けにビルドします。

POSIX vs ITRONのAPI

TOPPERS/ITRONのAPIはPOSIXとは全く異なる独自仕様(μITRON)です。cre_tsk()wai_sem()等の関数はPOSIXとの互換性はなく、移植には書き直しが必要です。RTOSのPOSIX準拠化はQNX・VxWorksが進めており、FreeRTOSはPOSIXラッパーを提供しています。

関連用語

参考リンク