投稿

ラベル(システムコールプログラミング)が付いた投稿を表示しています

Linuxシステムコール、POSIX mutex による排他制御

UNIX / Linux システムコール・プログラミング POSIX mutex による排他制御 mutex(ミューテックス)とは? mutex(ミューテックス)は、 Mut ual Ex clusion の略で訳せば相互排他となります。 Unix系のOSにはセマフォも存在していますが、セマフォとの違いとしては、 ・ミューテックスでは制御権を持てるのは1つだけ。 セマフォは複数の使用者を設定することが可能です。 セマフォで使用者を1つだけとした場合は、ミューテックスと同様の動作となります。このように0/1だけの操作となるようなセマフォ/ミューテックスはバイナリセマフォとも呼ばれます。 ・ミューテックスは所有者の概念を持っている セマフォでは、ロックするプログラムとアンロックするプログラムが別でも構わない。 (デッドロックの元になるので危険な実装なのでオススメはしない) しかし、ミューテックスでは所有者の概念があるためロックをかけた使用者しかアンロックできない。この部分がセマフォと異なる部分です。 これらの特徴から、ミューテックスはスレッド間の排他制御でよく利用されます。 ミューテックスを使ったロックは、ロック動作を行ったスレッドしかアンロックできません。 それでは、 早速コードを示します。 mutex.c #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <ctype.h> #include <unistd.h> pthread_mutex_t mutex; void* thread(void* arg); // スレッドエントリ // // Main thread. // int main(int argc, char **argv) { int ret; const char *arg1 = "Pneumonoultramicroscopicsilicovolcanoconiosis, Pneumonoultramicr...

Linuxシステムコール、POSIX スレッドの生成

UNIX / Linux システムコール・プログラミング POSIXスレッドの生成 スレッドとは? スレッドは、実行の単位ですがプロセスとは異なります。プロセスは、プロセスごとに独立した論理アドレス空間をもち、異なるプロセス間ではメモリの共有をしていません。 プロセスの生成に関しては下記を参照。  →  Linuxシステムコール、プロセスの生成 これに対してスレッドは、1つのプロセスのメモリ空間を共有する複数の実行単位となります。簡単に言ってしまえば、ある関数fx()を呼び出すときにスレッドとして呼び出すことでfx()を複数、同時並列的に実行できるようになります。 同様のことは fork()で別プロセスとして分岐し、fx()を呼ぶことで実現できますがfork()はプロセスのメモリイメージのコピー(実際にはコピーオンライトで異なる部分だけのコピーでその他は共有されます)が発生し、プロセス動作するためのスタック領域などが別個に確保されるためのコストが発生します。 スレッドは、このようなコストを必要としないという特徴があります。 また、スレッドはマルチコアのCPUの場合に特定のコアにスレッドを結びつけることも可能という特徴もあります。 また、通常c言語は、main() から始まりますが、この大本をメインスレッドとも表現します。 スレッドを動かしてみよう! それでは、実際にLinux上でスレッドを使ったコードについて解説していきたいと思います。 サンプルコードは下記のような処理を行います。 1.3つの新しいスレッドを、pthread_create() で生成します。  このとき、メインスレッドから起動時の引数として文字列を渡します。 2.それぞれのスレッドが同時並行で処理を行います。  このコードでは下記の2つの処理を実装しています。   スレッド関数1. 引数の文字列の長さ分タイマーウエイトを行う   スレッド関数2. 引数の文字列を大文字・小文字変換して返す 3.メインスレッドは、各スレッドの完了を待ち合わせます。  起動されたスレッドは終了時に、pthread_exit() を使って終了コードを通知します。  メインスレッドは、pthread_join() を使って各スレッドの終了状態を受け...

Linuxシステムコール、プロセスの生成

イメージ
UNIX / Linux システムコール・プログラミング プロセスの生成 Linuxのプロセスとは? Linuxでは、プログラムの実行単位がプロセスとして定義されています。 これまで、このシリーズでもプロセス間通信を取り上げてきましたが、そもそもプロセスとは何でしょうか? 今回は、このプロセスについて取り上げてみたいと思います。 さて、先程プログラムの実行単位がプロセスだと書きました。通常、我々はgccなどを使って作成された ”実行ファイル” をメモリに読み込んでプログラムを実行します。これがプロセスです。 簡単な”Hello World”を例題に説明します。 hello.c #include <stdio.h> int main(int argc, char** argv) { printf("Hello World\n"); } このプログラムをコンパイルします。     $ gcc hello.c -o hello とすると、実行ファイル hello が出来上がります。 実行するには、     $ ./hello とすれば、実行されます。 当然ですが、このときにナニが起きているのか? 今まで気にしたことは無いかもしれませんが、詳しく見てみましょう。 ”$ ./hello”では、実行ファイルをコマンドとして、シェルから実行しています。 このシェルというユーザーインターフェイスも1つのプロセスとしてメモリ空間に存在しています。 ユーザーが、”$ ./hello”と、入力するとシェルは、fork() というシステムコールを呼び出して別のプロセスのためのユーザー空間を生成します。 そして、exec()というシステムコールを使ってそのメモリ空間に実行ファイル”hello”を読み出して展開し、main()にジャンプすることで、実行ファイル”hello”が実行され、コンソールに"Hello World"が表示されます。 UNIX系のOSでは伝統的に fork()/exec() という2段階のシステムコールによってプロセスの生成を行います。 まず、fork()ですが、このシステムコールは呼び出したプロセスのコピーを作り出します。 実際のコードを見て...

Linuxシステムコール、INETドメイン ソケット

UNIX / Linux システムコール・プログラミング プロセス間通信(IPC)、ソケット通信/INETドメイン ソケット Unixドメイン ソケットとは? Unixドメイン ソケットは、ノード間のネットワーク通信を行うことができ、現代のインターネット環境の基盤技術の1つと言えます。 INETドメイン ソケットを利用することで実際のネットワーク上のプロトコルであるTCP/IPやUDP/IPを意識したプログラミングが可能になります。 ※今回のサンプルコードは、TCP/IPです。 基本的に、INETドメイン ソケットもUnixドメイン ソケットと同様の実装となりますが、異なるノード間での通信を実現するため、ネットワークアドレスやポート番号を意識する必要があります。 それでは、サーバー側のコードを以下に示します。 inet-dimain-server.c #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> // Make server socket. // int makeServer(int portNo) { int sfd; int ret; struct sockaddr_in serv; // Create server socket. if((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("ERROR:Server Socket()"); return -1; } // Bind socket memset(&serv, 0, sizeof(struct sockaddr_in)); ...

Linuxシステムコール、Unixドメイン ソケット

UNIX / Linux システムコール・プログラミング プロセス間通信(IPC)、ソケット通信/Unixドメイン ソケット Unixドメイン ソケットとは? Unixドメイン ソケットは、同一ノード内(1つのCPUで動作する1つのオペレーティング・システム内)で動作するプロセス間で、双方向の通信手段を提供します。 また、ソケットはクライアント/サーバー モデルで構成されており、1つのサーバーが複数のクライアントからの通信を受け入れる構造をとります。 まず、以下にサーバー側のサンプルコードを示します。 unix-dimain-server.c #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> // Make server socket. // int makeServer(char* sockName) { int sfd; int ret; struct sockaddr_un name; // Unlink, last running remove the socket. unlink(sockName); // Create server socket. if((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("ERROR:Server Socket()"); return -1; } // Bind socket memset(&name, 0, sizeof(struct sockaddr_un)); name.sun_family = AF_UNIX; strncpy(name.sun_path, sockName, sizeof(name.sun_path) - 1); if ((r...

Linuxシステムコール、セマフォの使い方

イメージ
UNIX / Linux システムコール・プログラミング プロセス間通信(IPC)、セマフォ セマフォとは? セマフォ、聞き慣れない言葉ですが大昔の鉄道の信号機のことです。 単線区間で列車同士が衝突しないようにするため、信号機が使われました。 意味合い的には同様で、コンピューター内のある資源(メモリなど)に別々のプロセスが同時にアクセスしないようにするために利用します。 セマフォはSYSTEM V IPC です。 この、セマフォを利用することで複数のプロセスが同期して処理を進めることができます。 Linuxのセマフォは、セマフォ集合として生成して利用します。 このため、1つのセマフォ集合に複数のセマフォを含めることができ、Linuxのセマフォはカウンティングセマフォであるため、複数の許可を与えることも可能です。 ただし、この記事のサンプルコードは1つの集合に1つだけのセマフォ、許可数は1つだけとなっています。 まず、セマフォ生成のサンプルコードを示します。 seminit.c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> #include <stdlib.h> union semun { int val; struct semid_ds *buf; unsigned short *array; }; int main() { key_t key; int semid; union semun arg; if ((key = ftok("sem.dat", 'S')) == -1) { perror("ftok"); exit(1); } /* create a semaphore set with 1 semaphore: */ if ((semid = semget(key, 1, 0666 | IPC_CREAT)) == -1) { p...

Linuxシステムコール、共有メモリの使い方

UNIX / Linux システムコール・プログラミング プロセス間通信(IPC)、共有メモリ 共有メモリとは? カーネルが管理するメモリ空間をユーザープロセスに ”見えるようにする” ことで、複数のプロセス間で同じメモリ空間を利用することができます。 プロセスからは通常のメモリと同様に見えるため、同じエリアに対しての同時書き込みなどについては、排他制御を行う、あるいはアトミックな操作とするなどの対応が必要になります。 共有メモリは SYSTEM V IPC です。 それでは、早速サンプルコードについて解説します。 今回のコードは2つのプログラムで構成されています。 shm_r.c 共有メモリは、shmget()システムコールにより共有メモリを取得します。このときの引数の指定で”ない場合は作成する”、パーミッションを与えるなどの指定を行います。生成された共有メモリにアクセスするため、shmat()システムコールでポインタを得るます。 以降、このプロセスはshmat()システムコールで得たポインタを通じ、自由に共有メモリにアクセスすることができます。 この、サンプルshm_r.cは、共有メモリを生成・アタッチすると1秒毎にメモリの更新をチェックし、更新された場合は内容を表示します。このときの更新内容が "Quit"であった場合、共有メモリをデタッチし、共有メモリを破棄します。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #define DATA_SIZE 2048 int main(void) { int id; char *shmData; char data[DATA_SIZE]; // Shared memory create a new with IPC_CREATE if((id = shmget(IPC_PRIVATE, DA...

Linuxシステムコール、シグナルの使い方

UNIX / Linux システムコール・プログラミング プロセス間通信(IPC)、シグナル シグナルとは? プロセスに対して非同期のイベント通知を行います。 プロセス側でシグナルハンドラを定義することで、非同期通知を受けることができます。 Ctrl+C で、動作中のコマンド(プロセス)を停止できますが、Ctrl+C は SIGINT というシグナルを発生させてプロセスの停止を行っています。 また、シグナルは SYSTEM V IPC です。 どのようにシグナルを使うか、下記にサンプルコードを示します。 このサンプルコードでは、 SIGINT と SIGTSTP の2つを受け取るシグナルハンドラを記述しています。 シグナルとシグナルハンドラは、sigaction()システムコールを使って設定されます。 同様のシステムコールとして、signal()が存在しますが、現代ではsigaction()の利用が推奨されています。 また、実装バージョンやアーキテクチャによっても動作や構造体が異なる場合がありますので注意してください。 実行した状態で、Ctrl + c(SIGINT) を押してもメッセージは表示されますがプログラムは停止しません。 Ctrl + z(SIGTSTP) を押すとプログラムが停止するようになっています。 $ ./signal I'm working... ^CCatch SIGNAL[2]    <-- Ctrl + c key in. Work harder!              <-- Signal handler message. Don’t stop. I'm working... I'm working... ^ZCatch SIGNAL[20]   <-- Ctrl + z key in. We have a break.        <-- Signal handler message. It’s goiung stop. I've had enough! $ signal.c #incl...

Linuxシステムコール、メッセージキューの使い方

UNIX / Linux システムコール・プログラミング プロセス間通信(IPC)、メッセージキュー メッセージキューとは? いわゆるメッセージボックスのような動作を行う IPC です。 プロセス間で一意に識別できるメッセージキューに対してメッセージを書き込み、受け取りができます。 また、メッセージキューは SYSTEM V IPC です。 メッセージキューを使うためには、まずメッセージキューを作成してユニークなキューIDを作成する必要があります。msgget()というシステムコールでキューを作成します。 メッセージを送信するためには msgsnd()、メッセージを受信するためには msgrcv()、最後にメッセージキューを削除するために msgctl() を使います。 メッセージの受信側プロセスのサンプルコードを下記に示します。 #include <sys/types.h> #include <stdio.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #include <unistd.h> #define MSGBUFSIZ 256 int main(int argc, char** argv) { int msqid; key_t msgkey; struct msgbuf{ long mtype; char mdata[MSGBUFSIZ]; }; struct msgbuf msgdata,*p; p = &msgdata; // Make Message queue key. msgkey = ftok("msgq.key",'X'); // Get Message queue ID. msqid = msgget(msgkey,IPC_CREAT|0666); // Message receive loop wile get "Quit" massage. while(1) { // Receive a Message. ...

IPC(Inter Process Communication) とは?

イメージ
IPCの基本: Linuxだけではなく、マルチタスクをサポートしたOSでは各タスク(=プロセス)が強調動作できるようにタスク間通信をサポートしています。  特に、仮想メモリをサポートし、カーネル空間とユーザー空間をプロテクション機構によって分断されたメモリ空間でユーザープロセスが動作するようなシステムでは、ユーザープロセス同士で直接データをやり取りすることができません。  また、FreeRTOSのように仮想アドレスでの動作では無いものの、複数スレッドの動作が可能となるような環境下では通信の確実性、つまり通信の途中でスケジューリングされてデータ転送が不完全な状態になる、といった事象をさけるためにもOSがタスク間通信をサポートしています。 Uinuxでは、Inter Process Communication = IPC としてサポートしています。  基本となるIPC関連のシステムコールには下記のものがあります。   ・メッセージキュー   ・共有メモリ   ・パイプ   ・ソケット   ・シグナル   ・セマフォ このうち、メッセージキュー、セマフォ、共有メモリは歴史的に SYSTEM V IPC と呼ばれ、ファイルシステムを利用しないIPCとなっています。 以下、それぞれの特徴について簡単に記述します。  メッセージキュー: いわゆるメッセージボックスのような動作を行う IPC です。 プロセス間で一意に識別できるメッセージキューに対してメッセージを書き込み、受け取りができます。  共有メモリ: カーネルが管理するメモリ空間をユーザープロセスに ”見えるようにする” ことで、複数のプロセス間で同じメモリ空間を利用することができます。 プロセスからは通常のメモリと同様に見えるため、同じエリアに対しての同時書き込みなどについては、排他制御を行う、あるいはアトミックな操作とするなどの対応が必要になります。 パイプ: コマンドのリダイレクションで使われるパイプです。 先のコマンドの標準出力を次のコマンドの標準入力にパイプでつなぐことでプロセス間の通信を行っています。 ソケット: インターネ...

Linuxシステムコール:Linuxのシステムコール実装

イメージ
Linuxのシステムコール実装  これまで述べてきたようにOS(カーネル)の持つ機能へのインターフェイスがシステムコールですが、システムコールの実態はカーネルの中にあります。このため、ユーザープログラムがシステムコールを呼び出すとユーザーモードからカーネルモードへの切り替わりが発生し、カーネル内部での処理完了でユーザーモードに復帰します。  例として、ファイルからデータを読み出す場合を見てみましょう。  ユーザープログラムがファイルを読むため、システムコールを呼び出します。このときCPUのソフトウェア割り込み、またはシステムコール命令が発行されることで、CPUはカーネルモードに移行します。システムコール命令を発行するとき、特定のレジスタに実行したいシステムコールの番号を設定しておきます。CPUはカーネルモードに切り替わるとシステムコール番号からシステムコールテーブルを参照して、カーネル内部の所定の処理を呼び出します。  この例では、ファイル読み込みが呼ばれ、その中で物理的なファイルを読み込み、ユーザー領域のバッファにデータを転送し、ユーザープログラムがシステムコールを呼んだ場所にリターンします。ユーザー空間にリターンすることで、CPUのモードもカーネルモードからユーザーモードに移行します。  システムコールを呼び出すことで、これらの一連の処理が実行されています。  少し余談になりますが、ライブラリもハードウェアとして実態のファイルにアクセスするためにシステムコールを使っています。なぜ同じような目的なのにわざわざライブラリにしているのでしょうか?  目的は2つあります。1つはシステムコールのラッパーとしての役割、もう一つは抽象化したシステムの提供です。  OSがWindowsである場合、ファイル読み込みのシステムコールは ReadFile() ですが、Windows上で動作するc言語のstdioで定義されているファイル読み込みは同じ fread() で、Linux上で動作する fread() と同様の動作をします。  このように、OSが変わっても同様のプログラミングが可能なようにする役割もライブラリは持っています。  また、抽象化とは、今回の例ではファイルの読み込みですが、ファイル読み込みはシステムコールでは read()...

Linuxシステムコール:Linuxのメモリ空間

イメージ
Linuxのメモリ空間  システムコールについて説明する前に、Linuxのメモリ空間について解説します。Linux上で動作するプログラムは、カーネル区間とユーザー空間に分けられています。  カーネル空間はその名が示すように、Linuxカーネルそのものが動作する空間で、物理アドレスを参照することができるため、すべてのハードウェア資源に直接アクセスすることができます。  これに対してユーザー空間は、論理アドレスに変換されたメモリ空間で動作するためユーザー領域として割り当てられた空間内でしか動作することができません。この、限られたメモリ空間はプロセスが起動する毎にカーネルが割り当てを行います。  なぜ、このような方式となっているのでしょうか?  下記のプログラムをLinux上で実行するとどうなるか想像がつきますね?お手元のLinuxでソースをコンパイルして実行してみてください。 #include #define MAX 4096  static char buf[16];  int main(int argc, char** argv) {      int i;      for(i = 0; i < MAX; i++) {          buf[i] = ' ';      }  } 実行すると、 「Segmentation fault (コアダンプ)」 と、表示されてプログラムは強制終了されてしまいます。 どうして強制終了されたのでしょうか?  このプログラムは、ユーザー空間で実行しました。カーネルはプロセスを起動する時に論理アドレスによる限られたメモリ空間を割り当てます。さきほどのプログラムは自分のメモリの外側までアクセスしたので、不正なメモリアクセスを行ったとして強制終了されました。  このようなシステム構成とすることで、ある1つのプログラムが問題を起こしても他のプログラムやハードウェアには問題を与えにくくすることができます。こうすることで、システム全体が暴走したり、突然停止するよう...

Linuxシステムコール:システムコールとは?

イメージ
システムコールとは?  OSが持つ機能を利用するための呼び出しを、Linuxではシステムコールと呼んででいます。OSによってはカネールコール、スーパーバイザコールあるいはサービスコールとも呼ばれることもあります。  一般的に、OSはコンピューター上の各種資源であるメモリやCPU、あるいはペリフェラルとして接続されているタイマーやファイル、表示や音声のデバイスなど多くのハードウェア・デバイスを管理しています。システムコールの目的の1つが、ユーザープログラムがこれらのハードウェアにアクセスするためのインターフェイスです。  例えば、メモリを動的に確保したいときに、OSが管理しているメモリ領域からメモリブロックを取得したり、不要になったときに返すなどのメモリを利用するためのシステムコールであったり、ファイルからデータを読み込んだり、書き込んだりするためのシステムコールなどがあります。これらのシステムコールは、物理的に存在しているハードウェアに何らかのアクセスを行います。そして、複数のプロセスから同時にアクセスを行った場合に調停を行う手段を提供していることもあります。  逆に物理的な存在ではない、プロセスやスレッドと言った実行単位の管理を行うのもOSのしごとです。現代のプログラミング環境は、マルチタスク環境が一般的となっているため、ユーザーが動作させるプログラムの単位であるプロセスやスレッドなどをどのような順番で実行させるか、どのCPUコアを割り当てるかといった実行管理や、動作中のプロセス間での通信手段をサポートするなど、システム全体が協調動作するためのサービスもOSが提供しています。やはりこのようなサービスのインターフェイスもシステムコールとして整備されています。  さて、ここまでは一般的なシステムコールについてでしたが、ここから先はLinuxのシステムコールについて解説しましょう。  Linuxは多くのシステムコールを持っています。手元のARM Ver4.14では397個、x86 32bitでは384個のシステムコールがあり、その中にはいくつか使われなくなった番号も含まれます。  ですが、他のUnix互換のOSと 完全に同じではありません 。例えば、FreeBSDなどは560個を超えるシステムコールを持っており、他方には存在しないシステ...