プロセス間通信、C ++スレッド通信の間にスレッド間通信

プロセス間通信を切り替え   https://www.cnblogs.com/LUO77/p/5816326.html

スレッド間通信   https://www.cnblogs.com/jobs1/p/10784021.html

スレッド間通信 

 

プロセスとスレッドの違い

プログラムは、それがどんな走りを持っていない、それだけだ意味、命令の単なる順序集合である静的なエンティティプロセスは、それがデータセットに実行することで、異なっています。プロセスは、動的なエンティティであり、それはそれ自身のライフサイクルを持っています、。リソースまたはイベントが待機状態にあり、タスクの元に戻すを完了することになっているのを待っている、原因スケジューリングと実行中に、作成して生産する予定です。すべては、与えられたデータセット上で動作するプログラムの動的なプロセスを反映しています。

プロセス:プログラムの同時実行が実装プロセスにおける配分と資源の管理の基本単位である、それは動的な概念、コンピュータのシステムリソースでの競争の基本単位です。

スレッド:、実行ユニットのプロセスであり、医療スケジューリングは、プロセス・エンティティです。独立して、基本的な単位のプロセスよりも小さいです。スレッドは、軽量プロセスとして知られています。

少なくとも1つのプロセスのプログラム、プロセス少なくとも一つのスレッド。

なぜスレッドはありますか?

  各プロセスは、独自のアドレス空間、そのプロセス空間を持つネットワークやマルチユーザスイッチで、サーバは通常、同時要求のユーザーの不確か数を大量に受信する必要がある、明らかにするためのプロセスを作成するための各要求は機能しません(システム大きなオーバヘッドユーザ要求に非効率的な応答)は、このようにオペレーティング・システム・スレッドの概念を導入します。

  • 実行スレッドが線形で、真ん中が中断または中止が、唯一のプロセスのためのリソースとプロセスがライン実行サービスを変更するにはするが、イベントスレッドの切り替えには、これらのリソースを保護する必要があります。
  • プロセスはシングルスレッド処理とマルチスレッドプロセスに分割され、ビューのシングルスレッドプロセスマクロ点は、マイクロの実行中にのみ単一の線形の実行です。マクロのマルチスレッド・プロセスは、複数の顕微鏡の操作を実行、線形です。

変更プロセスの変更は、実行のCPUスレッドを表しますが、リソースのプロセスに変化が発生していません。 

プロセスの違いをスレッド:

  • アドレス空間:このプロセスのアドレス空間を共有するのと同じプロセス内のスレッド間、およびプロセスは、別のアドレス空間です。
  • リソース、メモリ、I / O、CPUと同じプロセスで、このプロセスの他のスレッドのシェアとしてではなく、プロセス間のリソースが独立している:リソースを持っています。

     プロセスがクラッシュした後、それが保護されたモードで、他のプロセスに影響を与えませんが、全体のプロセススレッドの崩壊は死んでいます。だから、マルチプロセス、他のものより堅牢なマルチスレッド。

     プロセスを切り替え、資源消費、高効率。だから、プロセス内の頻繁な切り替え、より有効に活用スレッドに来るとき。特定の変数の同時動作を共有すると同時に、要求されたとしている場合同様に、唯一のスレッドが処理できないで使用することができます

  • 実行:シーケンスプログラムの実行順序と入口を実行している各個々のプロセス流入路。スレッドが独立して実行することはできません。しかし、それはアプリケーションによって制御される複数の実行スレッドを提供し、アプリケーションに応じて存在している必要があります。
  • スレッドは、プロセッサスケジューリングの基本単位であるが、プロセスではありません。
  • 両方が同時に実行することができます。

長所と短所:

  スレッド実行のオーバーヘッドは小さいが、管理や資源の保護を助長されていません。SMPマシン(デュアルCPUシステム)上で実行するスレッド。

  プロセス実行のオーバーヘッド大が、良いリソース管理および保護することができます。このプロセスは、前方のマシンにまたがることができます。

マルチスレッドを使用する際に、マルチプロセスを使用する場合は?

高い資源管理と保護要件は、コストや効率性、複数のプロセスの使用を制限しません。

所要時間資源の保護と管理が非常に高くない高効率の要件、頻繁な切り替え、複数のスレッドを使用しています。

 

プロセス間通信

マルチプロセス:

まず第一に、何が起こったのかについて話した後、最初のフォーク。

フォークで作成された新しいプロセスは子プロセス(子プロセス)と呼ばれています。この機能は、一度呼び出さが、二回返しています。両者の違いは、子プロセスの戻り戻り値が0であり、そして親プロセスの値は、IDの新しいプロセス(子プロセス)のプロセスである返します。子が親プロセスIDに戻った理由は次のとおりです。子プロセスのプロセスは、その子プロセスのIDをすべて取得できるように、プロセスは、プロセスの複数のではなく、関数とすることができるので任意の時間に呼び出すことができるので、子プロセスは、その理由のフォークは、それに0を返し、そのPIDを取得するためにGETPID();また、親プロセスのIDを取得する()getppidを呼び出すことができます(プロセスID 0は、常に交換プロセスで使用されるため、プロセスは、子プロセスID、0でありません)。

フォークした後、オペレーティングシステムは、父と息子の関係が、親プロセスの子プロセスの正確なコピーになりますが、それは、オペレーティングシステムに思える、彼らはより多くの兄弟の関係のように、2つのプロセスがコード空間を共有しているが、データは空間の独立しています、子データ空間の内容は、親プロセスの完全なコピーであり、また、命令ポインタと同じであり、子供は、親プロセスが現在の位置に実行されている子プロセスがフォークであるからプログラムカウンタPCは、すなわち、2つの方法の値と同じです実行の冒頭に復帰フォークが失敗した場合)、少し異なる、成功フォーク、子プロセスフォークの戻り値が0であれば、親プロセスフォークの戻り値は子プロセスのプロセスIDで、親プロセスはエラーを返します。
あなたが想像することができる。このように、2つのプロセスが同時に実行されており、かつ一斉に、フォークの後、彼らが分岐している、異なるジョブの通りでした。これはまた、フォークフォークと呼ばれる理由です

最初の実行に関しては、それは、オペレーティングシステム(スケジューリングアルゴリズム)に、だけでなく、問題に関連している可能性があり親と子のコラボレーションを必要に応じて、実用的なアプリケーションでは重要ではありませんが、原始的な方法で解決することができます。


 

通信の一般的な手段:

1.パイプラインパイプ:パイプは半二重通信モードでは、データは一方向にしか流れだけ間の遺伝的関係を有するプロセスで使用することができます。親族プロセスは通常、親子関係のプロセスを意味します。
2.名前付きパイプFIFO:名前付きパイプは、半二重通信であるが、それは無関係なプロセス間の通信を可能にします。
4.メッセージキューメッセージキュー:メッセージキューは、カーネルキュー識別子に格納されたメッセージによって、メッセージのリンクリストです。メッセージキューが少ない情報を克服シグナリングのみ搬送パイプ無地バイトストリームバッファのサイズが制限され、そして他の欠点。
5.共有ストレージ共有メモリ:共有メモリは、メモリは、プロセスによって作成されたこの共有メモリを他のプロセスがアクセスすることができる期間をマッピングしているが、複数のプロセスによってアクセスすることができます。IPC共有メモリは、それが具体的に設計された低効率を実行している他のプロセス間通信のためのものである、最速の方法です。これは、多くの場合、プロセス間の同期通信を達成するために組み合わせて使用される他の通信機構、例えば2つの信号、に関連しています。
前記セマフォセマフォ:セマフォはカウンタであり、複数のプロセスが、共有リソースへのアクセスを制御するために使用することができます。それは、多くの場合、共有リソースへのアクセスを防止するためのロック機構として使用される他のプロセスは、リソースにアクセスすることができ、プロセスです。したがって、メインならびにプロセス間の同期は、同じプロセス内の異なるスレッド間意味します。
7.ソケットソケット:ソリューションは、様々な他の通信機構と、ポートは、プロセス間通信機構であるスリーブは、その異なる間のプロセス間通信のために使用することができることです。
8.信号(sinal):信号は、受信処理を通知するために使用されるイベントが発生した、通信のより洗練された手段です。

 

信号:

信号Linuxシステムは、プロセス間の間の通信または操作するための機構であり、信号は、プロセスの状態を知ることなく、処理する任意の時点で送信されても​​よいです。プロセスが実行されていない場合、信号は、プロセスが実行を再開することを知って、カーネルアップによって保存され、これまで彼に渡されます。信号は、プロセスをブロックするように設定されている場合、それは閉塞それが解除されるまでときに遅延された信号は、プロセスに渡される渡します。

 

Linuxは、それぞれが異なる意味を表す、信号の数十を提供しています。信号を区別するためにそれらの値に依存して、通常の信号を表現するために、プログラムの信号名を使用します。Linuxシステム、およびその名の名前付き定数にこれらの信号に/usr/includebitssignum.hファイルで定義されています。一般的に、直接<signal.hに>好きなプログラムが含まれています。

 

ソフトウェアレベルでのメカニズムを中断するアナログ信号である、非同期通信では、信号は、ユーザ空間とカーネルプロセス間で直接対話することができます。カーネルはまた、空間に発生したシステムイベントをユーザーに通知するためにユーザ空間プロセスを通知する信号を使用することができます。二つのソースからの信号イベント:

1)このようなCLTR + Cを押すなどのハードウェアの供給源は、典型的には、割り込み信号SIGINTを生成します

2)ソフトウェアソース、システムコールまたはコマンド信号を使用して、例えば。最も一般的に使用されるシステムは、調達、setitimerが、sigation、このとき、sigqueue機能を殺す送信信号の関数です。ソースソフトウェアはまた、違法な操作と他の操作の数を含んでいます。

 

信号が生成されると、対応するユーザプロセス信号を生成3つの方法があります。

1)各指定されたデフォルトのアクションのために、Linuxの信号をデフォルトのアクションを実行します。

2)キャプチャ信号、信号処理機能は、信号が発生した場合、対応するハンドラを実行し、定義されています。

3)受信した信号は、プロセスへの影響を実行したくない信号を無視し、処理は、あなたはそれは、プロセスが任意の治療を通知しないされ、信号を無視することができ、続けてみましょう。

  2つのアプリケーションプロセスがある信号を捕捉し、システム管理者が任意の時点で中断したり、特定のプロセスの終了ができる可能にするためにSIGKILLとSEGSTOP、であることを無視することができません。

図は、一般的なLinuxコマンドを表し、

図1に示すように、信号伝送:

キー信号伝送システムが送られるどのようなプロセスとどのような信号を知るための信号を送信することができます。以下では、操作信号の共通機能であります:

例:自分が一時停止させる、raise関数を使用するために、子プロセスを合図SIGSTOPを送信するために終わりを発行する前に、子でない親プロセスを作るために、子プロセスを作成し、親プロセスは子プロセスにSIGKILLシグナルを送信し、信号動作キル機能を使用して子プロセスをこの信号は、子プロセスの終了を受けています。

図2に示すように、信号処理

当某个信号被发送到一个正在运行的进程时,该进程即对次特定的信号注册相应的信号处理函数,以完成所需处理。设置信号处理方式的是signal函数,在程序正常结束前,在应用signal函数恢复系统对信号的

默认处理方式。

3.信号阻塞

有时候既不希望进程在接收到信号时立刻中断进程的执行,也不希望此信号完全被忽略掉,而是希望延迟一段时间再去调用信号处理函数,这个时候就需要信号阻塞来完成。

 

例子:主程序阻塞了cltr+c的sigint信号。用sigpromask将sigint假如阻塞信号集合。

 

管道:

管道允许在进程之间按先进先出的方式传送数据,是进程间通信的一种常见方式。

管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:

1) 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

2) 匿名管道只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

3) 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

 

管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。

  • pipe用于相关进程之间的通信,例如父进程和子进程,它通过pipe()系统调用来创建并打开,当最后一个使用它的进程关闭对他的引用时,pipe将自动撤销。
  • FIFO即命名管道,在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。

无名管道:

pipe的例子:父进程创建管道,并在管道中写入数据,而子进程从管道读出数据

命名管道:

和无名管道的主要区别在于,命名管道有一个名字,命名管道的名字对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。

而无名管道却不同,进程只能访问自己或祖先创建的管道,而不能访任意访问已经存在的管道——因为没有名字。

 

Linux中通过系统调用mknod()或makefifo()来创建一个命名管道。最简单的方式是通过直接使用shell

mkfifo myfifo

 

 等价于

mknod myfifo p

 

以上命令在当前目录下创建了一个名为myfifo的命名管道。用ls -p命令查看文件的类型时,可以看到命名管道对应的文件名后有一条竖线"|",表示该文件不是普通文件而是命名管道。

使用open()函数通过文件名可以打开已经创建的命名管道,而无名管道不能由open来打开。当一个命名管道不再被任何进程打开时,它没有消失,还可以再次被打开,就像打开一个磁盘文件一样。

可以用删除普通文件的方法将其删除,实际删除的事磁盘上对应的节点信息。

例子:用命名管道实现聊天程序,一个张三端,一个李四端。两个程序都建立两个命名管道,fifo1,fifo2,张三写fifo1,李四读fifo1;李四写fifo2,张三读fifo2。

用select把,管道描述符和stdin假如集合,用select进行阻塞,如果有i/o的时候唤醒进程。(粉红色部分为select部分,黄色部分为命名管道部分)

 

 

 

在linux系统中,除了用pipe系统调用建立管道外,还可以使用C函数库中管道函数popen函数来建立管道,使用pclose关闭管道。

例子:设计一个程序用popen创建管道,实现 ls -l |grep main.c的功能

分析:先用popen函数创建一个读管道,调用fread函数将ls -l的结果存入buf变量,用printf函数输出内容,用pclose关闭读管道;

接着用popen函数创建一个写管道,调用fprintf函数将buf的内容写入管道,运行grep命令。

popen的函数原型:

FILE* popen(const char* command,const char* type);

 

参数说明:command是子进程要执行的命令,type表示管道的类型,r表示读管道,w代表写管道。如果成功返回管道文件的指针,否则返回NULL。

使用popen函数读写管道,实际上也是调用pipe函数调用建立一个管道,再调用fork函数建立子进程,接着会建立一个shell 环境,并在这个shell环境中执行参数所指定的进程。

消息队列:

消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。

消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。

可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。

消息队列的常用函数如下表:

进程间通过消息队列通信,主要是:创建或打开消息队列,添加消息,读取消息和控制消息队列。

例子:用函数msget创建消息队列,调用msgsnd函数,把输入的字符串添加到消息队列中,然后调用msgrcv函数,读取消息队列中的消息并打印输出,最后再调用msgctl函数,删除系统内核中的消息队列。(黄色部分是消息队列相关的关键代码,粉色部分是读取stdin的关键代码)

共享内存:

共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取错做读出,从而实现了进程间的通信。

 

采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝,对于像管道和消息队里等通信方式,则需要再内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。

一般而言,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时在重新建立共享内存区域;而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件,因此,采用共享内存的通信方式效率非常高。

共享内存有两种实现方式:1、内存映射 2、共享内存机制

1、内存映射

内存映射 memory map机制使进程之间通过映射同一个普通文件实现共享内存,通过mmap()系统调用实现。普通文件被映射到进程地址空间后,进程可以

像访问普通内存一样对文件进行访问,不必再调用read/write等文件操作函数。

例子:创建子进程,父子进程通过匿名映射实现共享内存。

分析:主程序中先调用mmap映射内存,然后再调用fork函数创建进程。那么在调用fork函数之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap函数的返回地址,这样,父子进程就可以通过映射区域进行通信了。

2、UNIX System V共享内存机制

IPC的共享内存指的是把所有的共享数据放在共享内存区域(IPC shared memory region),任何想要访问该数据的进程都必须在本进程的地址空间新增一块内存区域,用来映射存放共享数据的物理内存页面。

和前面的mmap系统调用通过映射一个普通文件实现共享内存不同,UNIX system V共享内存是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。

例子:设计两个程序,通过unix system v共享内存机制,一个程序写入共享区域,另一个程序读取共享区域。

分析:一个程序调用fotk函数产生标准的key,接着调用shmget函数,获取共享内存区域的id,调用shmat函数,映射内存,循环计算年龄,另一个程序读取共享内存。

(fotk函数在消息队列部分已经用过了,

根据pathname指定的文件(或目录)名称,以及proj参数指定的数字,ftok函数为IPC对象生成一个唯一性的键值。)

key_t ftok(char* pathname,char proj)

 

c++ 线程间通信方式

 

一:两个进程间的两个线程通信,相当于进程间通信

二:一个进程中的两个线程间通信

  通信方式:

1.互斥锁

  mutex;

  lock_guard (在构造函数里加锁,在析构函数里解锁)

  unique_lock 自动加锁、解锁

 

2.读写锁

  shared_lock

3.信号量

  c++11中未实现,可以自己使用mutex和conditon_variable 实现

  代码实现如下: 

#pragma once
#include <mutex>
#include <condition_variable>
class Semaphore
{
public:
 explicit Semaphore(unsigned int count); //用无符号数表示信号量资源 
 ~Semaphore();
public:
 void wait();
 void signal();
private:
 int m_count; //计数器必须是有符号数 
 std::mutex m_mutex;
 std::condition_variable m_condition_variable;
};
 
#include "Semaphore.h"
Semaphore::Semaphore(unsigned int count) :m_count(count) {
}
Semaphore::~Semaphore()
{
}
void Semaphore::wait() {
 std::unique_lock<std::mutex> unique_lock(m_mutex);
 --m_count;
 while (m_count < 0) {
  m_condition_variable.wait(unique_lock);
 }
}
void Semaphore::signal() {
 std::lock_guard<std::mutex> lg(m_mutex);
 if (++m_count < 1) {
  m_condition_variable.notify_one();
 }
}

4.条件变量

  condition_variable

 

一:两个进程间的两个线程通信,相当于进程间通信

二:一个进程中的两个线程间通信

  通信方式:

1.互斥锁

  mutex;

  lock_guard (在构造函数里加锁,在析构函数里解锁)

  unique_lock 自动加锁、解锁

 

2.读写锁

  shared_lock

3.信号量

  c++11中未实现,可以自己使用mutex和conditon_variable 实现

  代码实现如下: 

#pragma once
#include <mutex>
#include <condition_variable>
class Semaphore
{
public:
 explicit Semaphore(unsigned int count); //用无符号数表示信号量资源 
 ~Semaphore();
public:
 void wait();
 void signal();
private:
 int m_count; //计数器必须是有符号数 
 std::mutex m_mutex;
 std::condition_variable m_condition_variable;
};
 
#include "Semaphore.h"
Semaphore::Semaphore(unsigned int count) :m_count(count) {
}
Semaphore::~Semaphore()
{
}
void Semaphore::wait() {
 std::unique_lock<std::mutex> unique_lock(m_mutex);
 --m_count;
 while (m_count < 0) {
  m_condition_variable.wait(unique_lock);
 }
}
void Semaphore::signal() {
 std::lock_guard<std::mutex> lg(m_mutex);
 if (++m_count < 1) {
  m_condition_variable.notify_one();
 }
}

4.条件变量

  condition_variable

おすすめ

転載: www.cnblogs.com/yi-mu-xi/p/11024997.html