ポータビリティの素晴らしい世界へようこそ...というか、それがないのです。これら2つのオプションの詳細な分析を開始し、さまざまなオペレーティングシステムがそれらをどのように処理するかを詳しく調べる前に、BSDソケット実装がすべてのソケット実装の母であることに注意してください。基本的に、他のすべてのシステムは、ある時点(または少なくともそのインターフェイス)でBSDソケットの実装をコピーし、それを独自に進化させ始めました。もちろん、BSDソケットの実装も同時に進化したため、後でそれをコピーしたシステムは、以前にそれをコピーしたシステムには欠けていた機能を利用できました。BSDソケットの実装を理解することは、他のすべてのソケットの実装を理解するための鍵です。したがって、BSDシステムのコードを書く必要がない場合でも、それについて読む必要があります。
これらの2つのオプションを検討する前に、知っておくべき基本事項がいくつかあります。TCP / UDP接続は、次の5つの値のタプルによって識別されます。
{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}
これらの値の一意の組み合わせは、接続を識別します。その結果、2つの接続が同じ5つの値を持つことはできません。そうしないと、システムはこれらの接続を区別できなくなります。
ソケットのプロトコルは、関数でソケットを作成するときに設定されsocket()
ます。送信元アドレスとポートはbind()
関数で設定されます。宛先アドレスとポートはconnect()
関数で設定します。UDPはコネクションレス型プロトコルであるため、UDPソケットは接続せずに使用できます。それでも、それらを接続することは許可されており、場合によっては、コードや一般的なアプリケーションの設計に非常に有利です。コネクションレスモードでは、データが初めて送信されるときに明示的にバインドされなかったUDPソケットは、バインドされていないUDPソケットが(応答)データを受信できないため、通常、システムによって自動的にバインドされます。バインドされていないTCPソケットについても同じことが言え、接続される前に自動的にバインドされます。
ソケットを明示的にバインドすると、0
「任意のポート」を意味するポートにバインドできます。ソケットを既存のすべてのポートに実際にバインドすることはできないため、その場合、システムは特定のポート自体を選択する必要があります(通常、事前定義されたOS固有のソースポートの範囲から)。送信元アドレスにも同様のワイルドカードが存在します。これは「任意のアドレス」にすることができます(0.0.0.0
IPv4および::
IPv6の場合)。ポートの場合とは異なり、ソケットは実際には「すべてのローカルインターフェイスのすべての送信元IPアドレス」を意味する「任意のアドレス」にバインドできます。ソケットが後で接続される場合、ソケットは接続できず、同時にローカルIPアドレスにバインドできないため、システムは特定の送信元IPアドレスを選択する必要があります。宛先アドレスとルーティングテーブルの内容に応じて、システムは適切な送信元アドレスを選択し、「any」バインディングを選択した送信元IPアドレスへのバインディングに置き換えます。
デフォルトでは、2つのソケットを送信元アドレスと送信元ポートの同じ組み合わせにバインドすることはできません。送信元ポートが異なる限り、送信元アドレスは実際には無関係です。が当てはまる場合でも、にバインドsocketA
することは常に可能です。たとえば、FTPサーバープログラムに属し、別のFTPサーバープログラムにバインドされており、にバインドされている場合、両方のバインドが成功します。ただし、ソケットはローカルで「任意のアドレス」にバインドされる可能性があることに注意してください。ソケットがにバインドされている場合、既存のすべてのローカルアドレスに同時にバインドされます。その場合、バインドしようとしている特定のIPアドレスに関係なく、他のソケットをポートにバインドすることはできません。ipA:portA
socketB
ipB:portB
ipA != ipB
portA == portB
socketA
192.168.0.1:21
socketB
10.0.0.1:21
0.0.0.0:21
21
0.0.0.0
既存のすべてのローカルIPアドレスと競合します。
これまでに述べたことは、すべての主要なオペレーティングシステムでほぼ同じです。アドレスの再利用が始まると、OS固有のものになり始めます。上で述べたように、BSDはすべてのソケット実装の母であるため、BSDから始めます。
BSD
SO_REUSEADDR
バインドする前にソケットでが有効になっている場合、送信元アドレスとポートのまったくSO_REUSEADDR
同じ組み合わせにバインドされている別のソケットと競合しない限り、ソケットは正常にバインドできます。今、あなたはそれが以前とどのように違うのか疑問に思うかもしれませんか?キーワードは「正確に」です。主に、競合を検索するときのワイルドカードアドレス(「任意のIPアドレス」)の処理方法を変更します。SO_REUSEADDR
がないと、0.0.0.0は「任意のローカルIPアドレス」を意味するため、へSO_REUSEADDR
のバインドとその後のバインドは失敗します(エラーが発生します)。したがって、すべてのローカルIPアドレスがこのソケットで使用されていると見なされ、これにはも含まれます。とは完全に同じアドレスではないため、1つはすべてのローカルアドレスのワイルドカードであり、もう1つは非常に特定のローカルアドレスです。上記のステートメントは、バインドされている順序に関係なく当てはまることに注意してください。それがなければ常に失敗し、それがあれば常に成功します。socketA
0.0.0.0:21
socketB
192.168.0.1:21
EADDRINUSE
192.168.0.1
SO_REUSEADDR
0.0.0.0
192.168.0.1
socketA
socketB
SO_REUSEADDR
SO_REUSEADDR
より良い概要を提供するために、ここに表を作成し、考えられるすべての組み合わせをリストしてみましょう。
SO_REUSEADDRsocketAsocketB結果
-------------------------------------------------- -------------------
オン/オフ192.168.0.1:21192.168.0.1:21エラー(EADDRINUSE)
オン/オフ192.168.0.1:2110.0.0.1:21OK
オン/オフ10.0.0.1:21 192.168.0.1:21 OK
オフ0.0.0.0:21192.168.1.0:21エラー(EADDRINUSE)
オフ192.168.1.0:210.0.0.0:21エラー(EADDRINUSE)
オン0.0.0.0:21192.168.1.0:21OK
オン192.168.1.0:210.0.0.0:21OK
オン/オフ0.0.0.0:210.0.0.0:21エラー(EADDRINUSE)
上記の表はsocketA
、に指定されたアドレスにすでに正常にバインドされていることを前提としていますsocketA
。次にsocketB
作成され、SO_REUSEADDR
設定されるかどうかに関係なく、最後にに指定されたアドレスにバインドされsocketB
ます。Result
のバインド操作の結果ですsocketB
。最初の列に「」と表示されている場合ON/OFF
、の値はSO_REUSEADDR
結果とは無関係です。
わかりSO_REUSEADDR
ました。ワイルドカードアドレスに影響があります。知っておくと便利です。しかし、それはそれが持つ効果だけではありません。SO_REUSEADDR
そもそもほとんどの人がサーバープログラムで使用する理由でもある、もう1つのよく知られた効果があります。このオプションの他の重要な使用法については、TCPプロトコルがどのように機能するかを詳しく調べる必要があります。
ソケットには送信バッファがあり、send()
関数の呼び出しが成功した場合、要求されたデータが実際に送信されたことを意味するのではなく、データが送信バッファに追加されたことを意味するだけです。UDPソケットの場合、データは通常、すぐにではなくてもすぐに送信されますが、TCPソケットの場合、送信バッファーにデータを追加してからTCP実装が実際にそのデータを送信するまでに比較的長い遅延が発生する可能性があります。その結果、TCPソケットを閉じると、送信バッファに保留中のデータが残っている可能性があります。このデータはまだ送信されていませんが、コードはそれを送信済みと見なします。send()
呼び出しは成功しました。TCP実装が要求に応じてすぐにソケットを閉じていた場合、このデータはすべて失われ、コードはそれを認識しません。TCPは信頼できるプロトコルであると言われており、そのようにデータを失うことはあまり信頼できません。そのため、送信するデータがまだあるソケットは、TIME_WAIT
閉じると呼び出される状態になります。その状態では、保留中のすべてのデータが正常に送信されるまで、またはタイムアウトが発生するまで待機します。この場合、ソケットは強制的に閉じられます。
最大で、カーネルがソケットを閉じる前に待機する時間は、データがまだ送信中であるかどうかに関係なく、LingerTimeと呼ばれます。Linger Timeは、ほとんどのシステムでグローバルに構成可能であり、デフォルトではかなり長くなっています(2分は、多くのシステムで見られる一般的な値です)。また、ソケットオプションSO_LINGER
を使用してソケットごとに構成できます。このオプションを使用すると、タイムアウトを短くしたり長くしたり、完全に無効にしたりすることもできます。ただし、TCPソケットを正常に閉じることは少し複雑なプロセスであり、いくつかのパケットを送受信し(また、失われた場合に備えてそれらのパケットを再送する)、このプロセス全体を閉じる必要があるため、完全に無効にすることは非常に悪い考えです。リンガータイムによっても制限されます。残留を無効にすると、ソケットは飛行中のデータを失うだけでなく、通常は推奨されない適切な方法ではなく、常に強制的に閉じられる可能性があります。TCP接続が正常に閉じられる方法の詳細は、この回答の範囲を超えています。詳細については、このページを参照することをお勧めします。また、で残留を無効にした場合でもSO_LINGER
、ソケットを明示的に閉じずにプロセスが停止すると、BSD(および場合によっては他のシステム)は、構成した内容を無視して残留します。これは、たとえば、コードが単に呼び出す場合に発生しますexit()
(小さくて単純なサーバープログラムではかなり一般的です)またはプロセスがシグナルによって強制終了されます(これには、不正なメモリアクセスが原因で単にクラッシュする可能性が含まれます)。したがって、すべての状況でソケットが長引くことがないようにするためにできることは何もありません。
問題は、システムが状態のソケットをどのように処理するかということTIME_WAIT
です。が設定されていない場合SO_REUSEADDR
、状態のソケットはTIME_WAIT
引き続き送信元アドレスとポートにバインドされていると見なされ、新しいソケットを同じアドレスとポートにバインドしようとすると、ソケットが実際に閉じられるまで失敗します。これには時間がかかる場合があります。構成されたLingerTimeとして。したがって、ソケットを閉じた直後に、ソケットの送信元アドレスを再バインドできると期待しないでください。ほとんどの場合、これは失敗します。ただし、SO_REUSEADDR
バインドしようとしているソケットにが設定されている場合は、同じアドレスとポートにバインドされている別のソケットが状態にありますTIME_WAIT
すでに「半分死んでいる」ので、単に無視され、ソケットは問題なくまったく同じアドレスにバインドできます。その場合、他のソケットがまったく同じアドレスとポートを持っている可能性はありません。状態の死にかけているソケットとまったく同じアドレスとポートにソケットをバインドするとTIME_WAIT
、他のソケットがまだ「動作中」の場合に予期しない、通常は望ましくない副作用が発生する可能性があることに注意してください。ただし、これはこの回答の範囲を超えています。幸いなことに、これらの副作用は実際にはかなりまれです。
最後に知っておくべきことが1つありますSO_REUSEADDR
。バインドするソケットでアドレスの再利用が有効になっている限り、上記のすべてが機能します。もう一方のソケット(すでにバインドされているか、TIME_WAIT
状態にあるソケット)でも、バインド時にこのフラグが設定されている必要はありません。バインドが成功するか失敗するかを決定するコードはSO_REUSEADDR
、呼び出しに供給されたソケットのフラグのみを検査します。検査されたbind()
他のすべてのソケットについては、このフラグは調べられません。
SO_REUSEPORT
SO_REUSEPORT
ほとんどの人が期待SO_REUSEADDR
するものです。基本的に、以前にバインドされたすべてのソケットがバインドされる前に設定されている限りSO_REUSEPORT
、任意の数のソケットをまったく同じ送信元アドレスとポートにバインドできます。アドレスとポートにバインドされている最初のソケットが設定されていない場合、最初のソケットが再びバインドを解放するまで、この他のソケットが設定されているかどうかに関係なく、他のソケットをまったく同じアドレスとポートにバインドすることはできません。コード処理の場合とは異なり、現在バインドされているソケットが設定されていることを確認するだけでなく、バインド時にアドレスとポートが競合するソケットが設定されていることも確認します。SO_REUSEPORT
SO_REUSEPORT
SO_REUSEPORT
SO_REUESADDR
SO_REUSEPORT
SO_REUSEPORT
SO_REUSEPORT
SO_REUSEPORT
を意味するものではありませんSO_REUSEADDR
。つまり、ソケットSO_REUSEPORT
がバインドされたときに設定されておらず、まったく同じアドレスとポートにバインドされたときに別のソケットがSO_REUSEPORT
設定された場合、バインドは失敗します。これは予想どおりですが、他のソケットがすでに停止している場合も失敗します。TIME_WAIT
状態です。TIME_WAIT
ある状態の別のソケットと同じアドレスとポートにソケットをバインドできるようにするには、SO_REUSEADDR
そのソケットに設定するか、バインドする前に両方のSO_REUSEPORT
ソケットに設定しておく必要があります。もちろん、ソケットにとの両方を設定することは許可されています。SO_REUSEPORT
SO_REUSEADDR
SO_REUSEPORT
それが後で追加されたこと以外に言うことはあまりありませんSO_REUSEADDR
。そのため、このオプションが追加される前にBSDコードを「フォーク」した他のシステムの多くのソケット実装では見つかりません。このオプションの前に、2つのソケットをBSDのまったく同じソケットアドレスにバインドする方法。
Connect()EADDRINUSEを返しますか?
ほとんどの人はそれbind()
がエラーで失敗する可能性があることを知っていEADDRINUSE
ますが、アドレスの再利用を試してみるconnect()
と、そのエラーでも失敗する奇妙な状況に遭遇する可能性があります。どうすればいいの?Connectがソケットに追加するものであるリモートアドレスを、どのようにしてすでに使用しているのでしょうか。複数のソケットをまったく同じリモートアドレスに接続することはこれまで問題にならなかったので、ここで何が問題になっていますか?
返信の一番上で述べたように、接続は5つの値のタプルによって定義されます。覚えていますか?また、これらの5つの値は一意である必要があると言いました。そうでない場合、システムは2つの接続を区別できなくなります。アドレスを再利用すると、同じプロトコルの2つのソケットを同じ送信元アドレスとポートにバインドできます。つまり、これら5つの値のうち3つは、これら2つのソケットですでに同じです。これらのソケットの両方を同じ宛先アドレスとポートに接続しようとすると、2つの接続されたソケットが作成され、そのタプルは完全に同一になります。これは、少なくともTCP接続では機能しません(UDP接続はとにかく実際の接続ではありません)。2つの接続のいずれかでデータが到着した場合、システムはデータがどちらの接続に属しているかを認識できませんでした。
したがって、同じプロトコルの2つのソケットを同じ送信元アドレスとポートにバインドし、両方を同じ宛先アドレスとポートに接続しようとすると、実際には、接続しようとした2番目のソケットconnect()
のエラーで失敗します。 EADDRINUSE
5つの値の同一のタプルを持つソケットはすでに接続されています。
マルチキャストアドレス
ほとんどの人はマルチキャストアドレスが存在するという事実を無視しますが、それらは存在します。ユニキャストアドレスは1対1の通信に使用されますが、マルチキャストアドレスは1対多の通信に使用されます。ほとんどの人はIPv6について知ったときにマルチキャストアドレスに気づきましたが、この機能がパブリックインターネットで広く使用されたことはありませんでしたが、マルチキャストアドレスはIPv4にも存在していました。
SO_REUSEADDR
複数のソケットを送信元マルチキャストアドレスとポートのまったく同じ組み合わせにバインドできるため、マルチキャストアドレスの変更の意味。つまり、マルチキャストアドレスの場合は、ユニキャストアドレスの場合とSO_REUSEADDR
まったく同じように動作します。SO_REUSEPORT
実際、コードはマルチキャストアドレスSO_REUSEADDR
をSO_REUSEPORT
同じように扱います。つまり、すべてのマルチキャストアドレスをSO_REUSEADDR
意味し、その逆も同様です。SO_REUSEPORT
FreeBSD / OpenBSD / NetBSD
これらはすべて、元のBSDコードのかなり遅いフォークです。そのため、3つすべてがBSDと同じオプションを提供し、BSDと同じように動作します。
macOS(MacOS X)
本質的に、macOSは「ダーウィン」という名前のBSDスタイルのUNIXであり、BSDコード(BSD 4.3)のかなり遅いフォークに基づいており、後で(当時の)FreeBSDと再同期されました。 Mac OS 10.3リリースの5コードベース。これにより、Appleは完全なPOSIX準拠を取得できます(macOSはPOSIX認定済みです)。コアにマイクロカーネル( " Mach ")がありますが、カーネルの残りの部分( " XNU ")は基本的に単なるBSDカーネルです。そのため、macOSはBSDと同じオプションを提供し、BSDと同じように動作します。 。
iOS / watchOS / tvOS
iOSは、わずかに変更およびトリミングされたカーネル、多少簡素化されたユーザースペースツールセット、およびわずかに異なるデフォルトのフレームワークセットを備えた単なるmacOSフォークです。watchOSとtvOSはiOSフォークであり、さらに削除されています(特にwatchOS)。私の知る限り、これらはすべてmacOSとまったく同じように動作します。
Linux
Linux <3.9
Linux 3.9より前は、オプションのみがSO_REUSEADDR
存在していました。このオプションは、2つの重要な例外を除いて、BSDとほぼ同じように動作します。
リスニング(サーバー)TCPソケットが特定のポートにバインドされている限り、SO_REUSEADDR
そのポートを対象とするすべてのソケットでこのオプションは完全に無視されます。2番目のソケットを同じポートにバインドできるのは、設定せずにBSDでも可能だった場合のみSO_REUSEADDR
です。たとえば、ワイルドカードアドレスにバインドしてから、より具体的なアドレスにバインドすることはできません。BSDでは、を設定すると両方が可能になりますSO_REUSEADDR
。できることは、常に許可されているように、同じポートと2つの異なる非ワイルドカードアドレスにバインドできることです。この点で、LinuxはBSDよりも制限が厳しいです。
2番目の例外は、クライアントソケットの場合、SO_REUSEPORT
バインドされる前に両方にこのフラグが設定されている限り、このオプションはBSDの場合とまったく同じように動作することです。これを許可する理由は、複数のソケットをさまざまなプロトコルの同じUDPソケットアドレスに正確にバインドできることが重要であり、SO_REUSEPORT
3.9より前には存在しなかっSO_REUSEADDR
たため、そのギャップを埋めるためにそれに応じて動作が変更されたためです。 。その点で、LinuxはBSDよりも制限が少なくなっています。
Linux> = 3.9
Linux 3.9は、SO_REUSEPORT
Linuxにもオプションを追加しました。このオプションはBSDのオプションとまったく同じように動作し、バインドする前にすべてのソケットにこのオプションが設定されている限り、まったく同じアドレスとポート番号にバインドできます。
SO_REUSEPORT
それでも、他のシステムにはまだ2つの違いがあります。
「ポートハイジャック」を防ぐために、1つの特別な制限があります。同じアドレスとポートの組み合わせを共有するすべてのソケットは、同じ有効なユーザーIDを共有するプロセスに属している必要があります。したがって、あるユーザーが別のユーザーのポートを「盗む」ことはできません。SO_EXCLBIND
これは、欠落している/SO_EXCLUSIVEADDRUSE
フラグをいくらか補うための特別な魔法です。
さらに、カーネルは、他のオペレーティングシステムにはないソケットに対していくつかの「特別な魔法」を実行SO_REUSEPORT
します。UDPソケットの場合、データグラムを均等に分散しようとします。TCPリスニングソケットの場合、着信接続要求(呼び出しによって受け入れられるものaccept()
)を分散しようとします。同じアドレスとポートの組み合わせを共有するすべてのソケットに均等に。したがって、アプリケーションは複数の子プロセスで同じポートを簡単に開き、それを使用SO_REUSEPORT
して非常に安価な負荷分散を実現できます。
アンドロイド
Androidシステム全体はほとんどのLinuxディストリビューションとは多少異なりますが、そのコアではわずかに変更されたLinuxカーネルが機能するため、Linuxに適用されるものはすべてAndroidにも適用されるはずです。
ウィンドウズ
WindowsはSO_REUSEADDR
オプションのみを認識し、はありませんSO_REUSEPORT
。SO_REUSEADDR
Windowsのソケットでの設定は、BSDのソケットでの設定と同じように動作しますが、1つの例外がありますSO_REUSEPORT
。SO_REUSEADDR
Windows 2003より前では、他のソケットがバインドされたときにこのオプションが設定されていなかった場合でも、のソケットはSO_REUSEADDR
、すでにバインドされたソケットとまったく同じ送信元アドレスとポートに常にバインドされていました。この動作により、アプリケーションは別のアプリケーションの接続されたポートを「盗む」ことができました。言うまでもなく、これにはセキュリティに大きな影響があります。
Microsoftはそれを認識し、別の重要なソケットオプションを追加しましたSO_EXCLUSIVEADDRUSE
。ソケットに設定SO_EXCLUSIVEADDRUSE
すると、バインドが成功した場合、送信元アドレスとポートの組み合わせはこのソケットによって排他的に所有され、設定されている場合でも、他のソケットはそれらにバインドできませんSO_REUSEADDR
。
このデフォルトの動作はWindows2003で最初に変更され、Microsoftはこれを「拡張ソケットセキュリティ」(他のすべての主要なオペレーティングシステムでデフォルトとなる動作の面白い名前)と呼んでいます。詳細については、このページをご覧ください。3つの表があります。1つ目は従来の動作(互換モードを使用している場合でも使用中です!)、2つ目bind()
は同じユーザーが呼び出しを行ったときのWindows 2003以降の動作、3つ目はbind()
呼び出しはさまざまなユーザーによって行われます。
Solaris
SolarisはSunOSの後継です。SunOSは元々BSDのフォークに基づいており、SunOS 5以降はSVR4のフォークに基づいていましたが、SVR4はBSD、System V、およびXenixのマージであるため、ある程度までSolarisもBSDフォークであり、かなり早いもの。その結果、Solarisは知っているだけSO_REUSEADDR
で、はありませんSO_REUSEPORT
。動作はBSDのSO_REUSEADDR
場合とほとんど同じです。私の知る限りSO_REUSEPORT
、Solarisと同じ動作をする方法はありません。つまり、2つのソケットをまったく同じアドレスとポートにバインドすることはできません。
Windowsと同様に、Solarisにはソケットに排他的バインディングを与えるオプションがあります。このオプションの名前はSO_EXCLBIND
です。バインドする前にこのオプションがソケットに設定さSO_REUSEADDR
れている場合、2つのソケットのアドレスの競合がテストされていれば、別のソケットに設定しても効果はありません。たとえばsocketA
、ワイルドカードアドレスにバインドされ、有効になっsocketB
ていSO_REUSEADDR
て、非ワイルドカードアドレスとと同じポートにバインドされている場合、このバインドは、有効になっていsocketA
ない限り、通常は成功します。有効になっていない場合は、のフラグに関係なく失敗します。socketA
SO_EXCLBIND
SO_REUSEADDR
socketB
その他のシステム
システムが上記にリストされていない場合は、システムがこれら2つのオプションをどのように処理するかを調べるために使用できる小さなテストプログラムを作成しました。また、私の結果が間違っていると思われる場合は、コメントを投稿したり、誤った主張をしたりする前に、まずそのプログラムを実行してください。
コードの構築に必要なのは、ビットPOSIX API(ネットワーク部分用)とC99コンパイラー(実際には、ほとんどの非C99コンパイラーは、提供されている限り機能しますinttypes.h
。stdbool.h
たとえばgcc
、完全なC99サポートを提供するずっと前にサポートされています) 。
プログラムが実行する必要があるのは、システム内の少なくとも1つのインターフェイス(ローカルインターフェイス以外)にIPアドレスが割り当てられており、そのインターフェイスを使用するデフォルトルートが設定されていることだけです。プログラムはそのIPアドレスを収集し、それを2番目の「特定のアドレス」として使用します。
それはあなたが考えることができるすべての可能な組み合わせをテストします:
- TCPおよびUDPプロトコル
- 通常のソケット、リッスン(サーバー)ソケット、マルチキャストソケット
SO_REUSEADDR
socket1、socket2、または両方のソケットに設定
SO_REUSEPORT
socket1、socket2、または両方のソケットに設定
0.0.0.0
(ワイルドカード)、127.0.0.1
(特定のアドレス)、およびプライマリインターフェイスで見つかった2番目の特定のアドレスから作成できるすべてのアドレスの組み合わせ(マルチキャストの場合224.1.2.3
は、すべてのテストに含まれます)
結果を素敵な表に印刷します。知らないシステムでも機能します。そのSO_REUSEPORT
場合、このオプションは単にテストされません。
プログラムが簡単にテストできないのは、ソケットを強制してその状態に保つのが非常に難しいため、その状態のソケットにどのようSO_REUSEADDR
に作用するかです。TIME_WAIT
幸い、ほとんどのオペレーティングシステムは、ここではBSDのように動作するようであり、ほとんどの場合、プログラマーはその状態の存在を単に無視できます。
これがコードです(ここに含めることはできません。回答にはサイズ制限があり、コードはこの応答を制限を超えてプッシュします)。