ホームページ > 運用・保守 > Linuxの運用と保守 > Linuxは複数のプロセスを作成できますか?

Linuxは複数のプロセスを作成できますか?

青灯夜游
リリース: 2023-01-31 19:59:05
オリジナル
1754 人が閲覧しました

Linux は複数のプロセスを作成できます。 Linux は複数のプロセスをサポートし、複数のタスクを同時に処理して、システム リソースを最大限に活用できます。 Linux プロセス間の通信方法: 1. 名前なしパイプを使用する; 2. 名前付きパイプ (FIFO) を使用する; 3. 単一のシグナルを使用する; 4. 共有メモリを使用する; 5. メッセージ キューを使用する; 6. セマフォを使用する。

Linuxは複数のプロセスを作成できますか?

#このチュートリアルの動作環境: linux7.3 システム、Dell G3 コンピューター。

Linux は複数のプロセスを作成できます。

Linux は複数のプロセスをサポートします。マルチプロセス システムの利点の 1 つは、複数のタスクを同時に処理して、システム リソースを最大限に活用できることです。

#第 1 章 Linux マルチプロセスの概要

1.1 概要

1.1.1 プロセスの概念

Linux では、実行中のプログラムをプロセスと呼びます。


プログラム: 静的な概念。コンパイルされたバイナリ ファイルです。
プロセス: 動的概念。プログラムの実行中に、システムは対応するプロセス ## を自動的に実行します。 # プロセスには プロセス制御ブロック (PCB)、コード セグメント、およびデータ セグメントが含まれます。
3 つの部分 プロセス制御ブロック: Linux では、プロセスのステータス情報を記録する構造によって表されます
ゾンビプロセス
: 親プロセスが子プロセスより先に終了します 子プロセスを作成しても、子プロセスのリソースが親プロセスでリサイクルされていない場合、子プロセスは次のようになります。ゾンビ プロセスであり、ゾンビ プロセスは最終的にシステム内の INIT と呼ばれるプロセスによってリサイクルされます。
init プロセス (プロセス No. 1) は、システムの起動時に最初に実行されるプロセスであり、すべてのプロセスの祖先プロセスです。

#1.1.2 プロセス表示シェル コマンド

top
    動的プロセス情報の表示
  • #ps -ef プロセスの詳細情報を表示
  • pstree プロセス情報をツリー形式で表示
  • bg ハングする Putバックグラウンドで実行する開始プロセス
#1.2 プロセスの実行ステータス

1.2.1 実行状態

実行状態 (RUNNING): プロセスが CPU を占有しています。 準備完了状態 (実行中): プロセスは待機キュー内でスケジューリングを待っています。

    軽いスリープ (INTRUPTABLE): 現時点では、プロセスはイベントが発生するか、システム リソースがシグナルに応答するのを待っています。
  • ディープ スリープ (中断不可): 現時点では、プロセスはイベントまたは何らかのシステム リソースの発生を待っており、シグナルに応答できません。
  • 停止状態 (STOPPED): プロセスは現在中断されています。
  • ゾンビ状態 (ZOMBIE): 現時点ではプロセスをスケジュールできませんが、PCB は解放されていません。
  • DEAD: これは終了したプロセスであり、PCB は解放されます

1.2 .2 ユーザー モード/カーネル モードLinuxは複数のプロセスを作成できますか?

カーネル モード:

カーネル空間とも呼ばれ、カーネル プロセス/スレッドが配置される領域です。主にオペレーティング システムとハードウェアの相互作用を担当します。 ユーザー モード:

ユーザー空間

とも呼ばれ、ユーザー プロセス/スレッドが配置される領域です。主にユーザープログラムの実行に使用されます。

1. 違い

Linuxは複数のプロセスを作成できますか? カーネル状態: 実行中のコードには制限がなく、CPU は任意の命令を実行できます。

ユーザー モード:

CPU をスケジュールできず、ハードウェアに直接アクセスできません。実行中のコードは CPU による多くのチェックを受ける必要があり、カーネル データやプログラムに直接アクセスすることはできません。つまり、カーネル モード スレッドのように有効なアドレスにアクセスすることはできません。
オペレーティング システムはユーザー プログラムを実行するとき、主にユーザー モードで動作し、完了する権限がないタスクを実行する場合にのみカーネル モードに切り替わります。
2. ユーザー モードとカーネル モードを区別する理由

ユーザー プロセスの誤操作やシステムへの悪意のある損傷を防ぐ保護メカニズム

  • リソースの一元管理を確保し、リソース使用量の競合を削減します。

  • 3. ユーザーモードからカーネルモードへの切り替え

(1) システムコール(アクティブ)

システムコール(システムコール)は、オペレーティングシステムに何らかの特権操作の実行を要求するためにオペレーティングシステムによってユーザープロセスに提供されるインターフェース、つまりユーザープロセスにサービスを提供するウィンドウです。 Linux では、man syscalls コマンドを使用して、Linux が提供するすべてのシステム コール API インターフェイスを表示できます。 ユーザー状態では特定のタスクを完了できないため、ユーザー状態はカーネル状態への切り替えを要求し、カーネル状態はユーザーに対して特別に開かれた割り込みを通じて切り替えを完了します。

(2) 周辺機器割り込み(パッシブ)
周辺機器から割り込み信号が送信され、割り込みが発生すると実行中のプロセスが一時停止し、OS カーネルが割り込み処理を処理します。割り込みの前に、CPU はユーザー モード プログラムを実行していました。これはユーザー モードからカーネル モードへの切り替えに相当します。
割り込みは、CPU 制御がオペレーティング システムに確実に渡され、オペレーティング システムが特定の操作を実行できるようにするために使用されます。

(3) 例外(パッシブ)
ユーザプログラム実行時に未知の例外が発生した場合、ユーザプログラムからカーネルに切り替えて例外処理を行います。 . プログラムはカーネル状態に切り替わります。

#1.3 プロセスインターフェース関数

1.3.1 プロセス作成 fork/vfork

1, fork(), vfork()

(1) 新しく作成されたプロセスは子プロセスと呼ばれ、親プロセスのすべてのリソースをコピーします (作成時に 1 回だけコピーされます。##) # 以降グローバルでは変数の値が異なります
)、親プロセスと子プロセスのどちらが先に来るかは不明です。

#include <unistd.h>
pid_t fork(void);
  		返回值:  > 0表示处于父进程中 这个时候的返回值就是**子进程进程id号**
                  ==0 表示处于子进程中
                  < 0 创建进程出错
                  
#include <sys/types.h>
  		 #include <unistd.h>
		pid_t vfork(void);
ログイン後にコピー
(2) **vfork()** 子プロセスは親プロセスのすべてのリソースを共有します。

子プロセスが最初に実行されなければなりません

その後、親プロセスが実行されます(sleep() を追加して人為的に干渉しても役に立たない) (3) 注 子プロセスで exit() を使用した場合と使用しない場合の結果はまったく異なります。
親子プロセスで sleep() を使用すると、CPU タイムスライスを放棄することも異なります
wait() を親プロセスで使用するかどうかで、waitpid() の結果も異なります

#(4) プロセス切り替えの実行 Linuxは複数のプロセスを作成できますか?


Linuxは複数のプロセスを作成できますか?##1.3.2 プロセス終了 exit/_exit

1、exit()、_exit()
    #include <stdlib.h>
    void exit(int status); 
	void _exit(int status);
	参数:
          status --->进程退出时的状态
          status在实际编写程序中是可以自己约定的:
          比如:  exit(2)----》出现打开文件错误
                  exit(3)----》出现段错误(逻辑错误)
                  exit(0)----》正常退出
		  返回:  void
	
	区别:
          exit()在退出的时候会刷新IO缓冲区,然后才退出(负责任的退出)
          _exit() 直接退出(不负责任的退出)
ログイン後にコピー

1.3.3 子プロセスが終了するのを待ちます (親プロセスはリソースをリサイクルします) wait/waitpid

1, wait()
	#include <sys/wait.h>
    pid_t wait(int *stat_loc);
   返回值:你回收的那个子进程的id号
   参数:stat_loc --》保存子进程退出时的状态信息(不仅仅只是返回值)
  		 stat_loc里面不仅仅只是保存了exit退出时的数值,它还保存了子进程退出时是哪个信号让它退出的,		
  	    出错了是什么原因导致的。
ログイン後にコピー

2.waitpid()

pid_t waitpid(pid_t pid, int *stat_loc, int options); 回收子进程/进程组
    参数: pid ----》你指定要回收的那个子进程的id
           		<-1   等待进程组号为-pid中的某个子进程退出                                 					
           	   	 -1      等待任意一个子进程
                ==0     	等待本进程组中的某个子进程退出
			    > 0 		等待PID为pid的进程
			stat_loc-----》存放子进程退出状态(可为NULL) 	
            options ----》一般设置为0
						WNOHANG  当没有子进程时立即返回
						WUNTRACED 当有子进程被暂停时立即返回
                        WCONTINUED  当有子进程收到SIGCONT立即返回
	返回值:-1 执行失败
			> 0 成功 返回值为被回收的进程的PID
			0  指定了WNOHANG,且没有已退出的子进程
ログイン後にコピー
Linuxは複数のプロセスを作成できますか?

1.3.4 プロセスの ID を取得します – getpid

(1)获取自己的id    getpid()
		#include <unistd.h>
		pid_t getpid(void);  返回值:就是该进程的id号
(2) 获取父进程id    getppid()
         #include <unistd.h>
         pid_t getppid(void);  返回值:就是父进程的id号
ログイン後にコピー

第 2 章 Linux のマルチプロセス通信方式

プロセス間通信かスレッド間通信か。すべては 1 つの問題を解決することです: 共有リソースの割り当て (共有リソースへのさまざまなプロセス/スレッドのアクセスを調整する)

2.1 プロセス間通信方法#1. 従来のプロセス間通信方法##名前のないパイプ

#名前付きパイプ

##Signal

  • 2. System V IPC オブジェクト
  • 共有メモリ
  • メッセージ キュー

セマフォ

  • 3. BSD
  • ネットワークソケット (ソケット)

2.1.1 名前のないパイプパイプ
  1. 1. 特徴: 最も独創的なプロセス間通信メソッド
アフィニティ関係

(父子プロセス、兄弟プロセス) を持つプロセス間でのみ通信できます; 名前はありません (存在します); はい 共有内に作成されますLinux と Windows の間では (パイプ ファイルはまったく生成されません)、有名なパイプは動作しません (パイプ ファイルは生成されます);

半二重通信。


2. 名前のないパイプの使用 (1) パイプ()の作成

    #include <unistd.h>
   int pipe(int fildes[2]);
   参数:fildes[2]里面放的是两个文件描述符fildes[0],fildes[1]
         fildes[0] 读端
         fildes[1] 写端
   返回值:成功返回0    失败返回-1
ログイン後にコピー

(2) パイプ情報の送受信
myid = fork();        //创建子进程
if(myid == 0)
{
	write(fd[1],"dad,thanks!",20); //子进程向父进程发送消息
	close(fd[1]);
	close(fd[0]);
	exit(0);
}
else if(myid > 0)  
{
	read(fd[0],buf,20);   //父进程阻塞接受子进程消息
	printf("buf is:%s\n",buf);
	close(fd[1]);
	close(fd[0]);
}
ログイン後にコピー

##2.1 2 名前付きパイプ (FIFO)


1. 機能: 任意の 2 つのプロセス間で使用可能

は Linux と Windows 間の共有には作成できません。

書き込みのアトミック性を確保します (アトミック性: 実行しないか、外部からの干渉なしに一度​​に実行します);

有名なパイプ

は作成を上書きできません

(通常、アクセス() 関数は、コードが存在するかどうかを確認するために使用されます。同じ名前のパイプがすでに存在する場合、再度作成することはできません);
  • 使用後は忘れずに閉じてください;
  • パイプが
  • 読み取り専用の場合
  • モードで開かれた場合、別のプロセスがパイプを 書き込み専用 モードで開くまでブロックされ、その後はブロックされません。読み書きモードではブロックされません。
  • 全二重通信、半二重通信。
  • 2. 有名パイプの利用 (1) mkfifo()の作成
       #include <sys/types.h>
       #include <sys/stat.h>
       int mkfifo(const char *pathname, mode_t mode);
      参数:pathname 有名管道的路径名
           	mode:权限  0666
    	  返回值:0 成功
    			  -1 失败
    ログイン後にコピー
    (2) FIFO処理情報の送受信
  • fifo_read.c :-----------》
    
    #define FIFO1  "myfifo1"
    #define FIFO2  "myfifo2"
    int main(void) {
    	int my_fd,fd1,fd2;
    	char r_buff[30];
        char w_buff[30];
    	bzero(r_buff,30);
    	
    	if(access(FIFO1,F_OK)==-1) {
    		my_fd = mkfifo(FIFO1,0664);	//创建管道1
    		if(my_fd == -1) {
    			perror("failed!\n");
    			return -1;
    		}
    	}
    	
    	if(access(FIFO2,F_OK)==-1) {
    		my_fd = mkfifo(FIFO2,0664);	//创建管道2
    		if(my_fd == -1) {
    			perror("failed!\n");
    			return -1;
    		}
    	}
    		
    	fd1 = open(FIFO1,O_RDONLY); //只读打开管道1,获取管道文件描述符
    	if(fd1==-1) {
    		printf("open fifo1 file failed!\n");
    		exit(0);
    	}
    	fd2 = open(FIFO2,O_WRONLY);	//只写打开管道2,获取管道文件描述符
    	if(fd2==-1)	{
    		printf("open fifo2 file failed!\n");
    		exit(0);
    	}
    	
    	while(1) {
    		bzero(r_buff,30);
    		read(fd1,r_buff,sizeof(r_buff));  //读取管道1的消息
    		printf("client receive message  is: %s\n",r_buff);
    		printf("client please input a message!\n");
    		fgets(w_buff,30,stdin);
    		write(fd2,w_buff,30);  //发送信息给管道2
    	}
    	close(fd2);
    	close(fd1);
    	
    	return 0;
    }
    
    fifo_write.c :-----------》
    #define FIFO1  "myfifo1"
    #define FIFO2  "myfifo2"
    int main(void)
    {
    	int my_fd,fd1,fd2;
    	char w_buff[30];
    	char r_buff[30];
    	bzero(w_buff,30);
    	if(access(FIFO1,F_OK)==-1) {
    		my_fd = mkfifo(FIFO1,0664);
    		if(my_fd == -1) {
    			perror("failed!\n");
    			return -1;
    		}
    	}
    	
    	if(access(FIFO2,F_OK)==-1) {
    		my_fd = mkfifo(FIFO2,0664);
    		if(my_fd == -1) {
    			perror("failed!\n");
    			return -1;
    		}
    	}
    	
    	fd1 = open(FIFO1,O_WRONLY);
    	if(fd1==-1) {
    		printf("open fifo1 file failed!\n");
    		exit(0);
    	}
    	fd2 = open(FIFO2,O_RDONLY);
    	if(fd2==-1) {
    		printf("open fifo2 file failed!\n");
    		exit(0);
    	}
    	while(1) {
    		bzero(w_buff,30);
    		printf("server please input a message!\n");
    		fgets(w_buff,30,stdin);  
    		write(fd1,w_buff,strlen(w_buff));  //写入消息到管道1文件
    		read(fd2,r_buff,30);  //读取信息从管道2
    		printf("server receive message is:%s\n",r_buff);
    	}
    	close(fd1);
    	close(fd2);
    	
    	return 0;
    }
    ログイン後にコピー
  • ##2.1.3 単一のシグナル
##プログラム (プロセス) が実行されているとき、外部の世界から時々プログラムにシグナルが送信されます。このとき、プログラムは 2 つの選択肢に直面します。 # 無視します (ブロック) /Ignore)

ブロッキング: シグナルを一時停止し、応答する前にプログラムの実行が終了するまで待機することを指します
無視: このシグナルを破棄します

応答します

1 Linux にはどのようなシグナルがありますか: kill -l view

 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX
ログイン後にコピー

(1)1到31号信号称作非实时信号:不支持队列(如果同时来了多个信号,响应是没有规律)
(2)用户自定义的信号 10) SIGUSR1 12) SIGUSR2
(3) 34到64号信号叫做实时信号:支持队列,是linux系统中后面添加进来的信号
信号类似于中断: 硬件 软件
以上信号有两个很特殊:SIGKILL,SIGSTOP不能够被忽略,也不能被阻塞

2、信号相关的操作函数
(1)发送信号kill()

 #include <signal.h>
 int kill(pid_t pid, int sig);
 参数:
   pid ----》进程的id
			正数:要接收信号的进程的进程号
			0:信号被发送到所有和pid进程在同一个进程组的进程
			-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
   sig ----》信号名字
   返回值:0 成功
		   -1 出错
ログイン後にコピー

(2)信号的捕捉 signal()

 #include <signal.h>
 void (*signal(int sig, void (*func)(int)))(int);  // SIGKILL
         参数:sig ----》你需要捕捉的那个信号
               void (*func)(int) ----》函数指针,回调函数,捕捉到对应的信号的时候就调用该函数;第二个参数除了可以传递一个函数指针意外,还可以使用以下两个宏定义:
                SIG_IGN ---->你捕捉到的那个信号会被忽略
                SIG_DFL-----》你捕捉的信号会采用系统默认的方式响应
		返回值:成功:设置之前的信号处理方式
			出错:-1
ログイン後にコピー

(3)等待信号 pause()

#include <unistd.h>
int pause(void);
	返回值:-1 把error值设为EINTR
			0 成功
ログイン後にコピー

(4)信号的阻塞
每个进程都有属于它自己的一个信号掩码(也就是该进程在运行的过程中会阻塞掉的那些信号就被称作信号掩码)。
关于信号掩码操作的一系列函数:

#include <signal.h>

  		int sigemptyset(sigset_t *set):清空信号掩码
        int sigfillset(sigset_t *set):将所有的信号添加到信号掩码中
        int sigaddset(sigset_t *set, int signum):将特定的信号添加到信号掩码中 
  		int sigdelset(sigset_t *set, int signum):将特定的信号从掩码中删除
  		int sigismember(const sigset_t *set, int signum):判断某个信号在不在该掩码中
		参数:sigset_t ----》存储被进程阻塞的信号
ログイン後にコピー

(5)配置信号掩码 sigprocmask()—阻塞或解除阻塞信号

    #include <signal.h>
 	int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)
	参数:
      how ---》SIG_BLOCK 将set所包含的信号添加到原来的信号掩码中
              SIG_SETMASK 用set去替换原来的信号掩码
              SIG_UNBLOCK 将set中包含的信号从原来的掩码中删除
      set ---》新的信号掩码
      oset ---》原本的信号掩码
 				原本进程中信号掩码包含了:SIGINT ,SIGCONT
ログイン後にコピー

(6)捕捉指定信号并获取信号携带信息sigaction()

#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
参数:
  	sig ---》你要捕捉的那个信号
    act ---》你需要捕捉的信号对应的响应函数就定义在这个结构体
    oact ---》原来的
    struct sigaction
    {
        void(*) (int)     sa_handler ----》 信号的响应函数
        sigset_t          sa_mask  ---》信号的掩码
        int               sa_flags ----》  SA_SIGINFO
        void(*) (int, siginfo_t * ,void )---》信号的响应函数
     }
	sa_flags ---》等于SA_SIGINFO,那么信号的响应函数是void(*) (int, siginfo_t * ,void )
          		不等于,那么信号的响应函数是void(*) (int) 
   siginfo_t---》/usr/include/i386-linux-gnu/bits/siginfo.h 保存的是信号的状态信息,信号的标号,发送该信号的进程的id等等这些
ログイン後にコピー

2.1.4 共享内存

查看共享内存: ipcs -m
删除共享内存: ipcrm -m 共享内存的id
SYSTEM-V ipc通信方式:共享内存、信号量、消息队列。

1、共享内存特点:跟mmap()思想上有些类似

  • 在进程通信方式中共享内存是效率最高的,进程可以直接读写内存,而不需要任何数据的拷贝
  • 如果代码不人为地删除共享共享内存,那么程序退出的时候它还在;
  • 多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

Linuxは複数のプロセスを作成できますか?

2、共享内存对应的一系列操作函数
(1)创建共享内存:shmget()

#include <sys/shm.h> 
int shmget(key_t key, size_t size, int shmflg);
返回值:成功—共享内存对象的mid(标识符) 出错—-1
参数:key----》创建共享内存需要用到的键值
      size----》内存空间的大小(字节)
      shmflg----》设置属性  IPC_CREAT   IPC_EXCL    0666组合
      
key键值的获取有两种方法:
 **方法一**:使用ftok()生成键值
            #include <sys/types.h>
            #include <sys/ipc.h>
            key_t ftok(const char *pathname, int proj_id);
            参数:pathname----》 路径名
                  proj_id----》整数
                 ftok(“.”  ,  11)  生成一个唯一的key值
            进程1:ftok(“.”  ,  11)  ----》shmget( 100);.............
       		进程2:ftok(“/home/gec”  ,  11)  ----》shmget( 106); 
				   无法通信,要确保键值一致才能通信
       
 **方法二:**不使用ftok(),程序员自己写个数字  
          shmget((key_t)1234, size_t size, int shmflg);
ログイン後にコピー

(2) 映射共享内存到用户空间 shmat()

#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
 返回值:成功—映射到用户空间的那片地址的首地址   出错—-1
 参数:shmid ----》使用shmget的返回值
      shmaddr----》一般设置为NULL 系统自动分配
       shmflg----》 SHM_RDONLY:共享内存只读
			        一般设置为0: 共享内存可读写 
			        if it is 0 and the calling process has read and write permission, the segment is attached for reading and writing.
ログイン後にコピー

(3)解除映射:shmdt()

#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数:shmaddr----》 shmat()共享内存映射后的地址
返回值:成功—0 	出错—-1
ログイン後にコピー

(4)删除共享内存:shmctl()

 #include <sys/shm.h>
 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
 参数: shmid----》共享内存的id
        cmd----》IPC_RMID    删除共享内存
                 IPC_STAT  (获取对象属性)
       			 IPC_SET (设置对象属性)
        *buf----》指定IPC_STAT/IPC_SET时保存共享内存的状态信息
		返回值:成功	失败—-1
ログイン後にコピー

Linuxは複数のプロセスを作成できますか?

Linuxは複数のプロセスを作成できますか?

3、共享内存简单示例

shm_write.c :----------》
int main() {
	int shmid;
	int *p;
	
	// 创建共享内存
	shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
	if((shmid == -1)&&(errno == EEXIST)) {
		shmid = shmget((key_t)456,1024,0666);
	}
	
	// 映射共享内存到进程
	p = (int *)shmat(shmid,NULL,0);
	*p = 10;

	// 解除映射
	shmdt(p);
	
	// 删除内存
	//shmctl(shmid,IPC_RMID,NULL);
	return 0;
}

shm_read.c :----------》
int main() {
	int shmid;
	int *p;
	
	// 创建共享内存
	shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
	if((shmid == -1)&&(errno == EEXIST)) {
		shmid = shmget((key_t)456,1024,0666);
	}
	
	// 映射共享内存到进程
	p = (int *)shmat(shmid,NULL,0);
	printf("p is :%d\n",*p);
	
	// 解除映射
	shmdt(p);

	// 删除内存
	shmctl(shmid,IPC_RMID,NULL);
	
	return 0;
}
ログイン後にコピー

2.1.5消息队列

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列由消息队列ID来唯一标识
消息队列可以按照类型来发送/接收消息
消息队列的操作包括创建或打开消息队列、添加消息、读取消息和控制消息队列

Linuxは複数のプロセスを作成できますか?

1、消息队列的特点
写入消息队列的信息,在编写程序的时候会人为的去设置消息的类型(用整数来表示),目的是为了其它进程在读取信息的时候能够准确地通过消息的类型判断要读取的信息。

2、消息队列操作的系列函数
(1)消息队列的创建 msgget()

 #include <sys/msg.h>
 int msgget(key_t key, int msgflg);
ログイン後にコピー

Linuxは複数のプロセスを作成できますか?

(2)消息队列的收发信息msgsnd()msgrcv()

 #include <sys/msg.h>
 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
	参数:void *msgp ----》你要发送信息就存储在这个指针中
                  在实际的编程中我们都是定义一个结构体来存储信息
                  struct msgbuf {
                      long mtype;  ----》消息的类型
                      char mtext[100]; ----》消息的内容
                    }
                    msgsz ----》消息的长度,大小
                    msgflg ----》设置为0    除开以上三种宏定义之外的----阻塞读写
ログイン後にコピー

1Linuxは複数のプロセスを作成できますか?

(3)消息队列的删除 msgctl()

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
ログイン後にコピー

1Linuxは複数のプロセスを作成できますか?

3、消息队列通信简单示例

pthread1.c :-----------》
#define SIZE 64
//数据接收结构体
struct msg_rv
{
	int mtype;
	char msg[50];
	
};

//数据发送结构体
struct msg_snd
{
	int mtype;
	char msg[50];
};

int main(void) {
	int msgid;
	struct msg_rv data;
	struct msg_snd snddata;
	char buff[50];

	//获取msgid
	msgid = msgget((key_t)123,IPC_CREAT|0666);
	if(msgid == -1)	{
		printf("msgid failed!\n");
		return -1;
	}

	data.mtype = 88;
	snddata.mtype = 89;
	
	while(1) {
		bzero(buff,50);
		printf("please input data!\n");
		fgets(buff,50,stdin);	
		strcpy(snddata.msg,buff);
		if(strncmp(snddata.msg,"end",3)==0) {
			break;
		}
		
		msgsnd(msgid,(void *)&snddata,strlen(buff)+4,0);//得到的值发送出去
		usleep(20);
		printf("run here!\n");
		
		if(msgrcv(msgid,(void *)&data,sizeof(struct msg_rv),data.mtype,0)==-1) {
			printf("msgsnd failed!\n");
			return -1;
		}
		
		printf("receive data:%s\n",data.msg);
		if(strncmp(data.msg,"end",3)==0) {
			break;	
		}	
	}
	
	//撤消消息队列
	msgctl(msgid,IPC_RMID,0);
	return 0;
}

pthread2.c :------------------------》
#define SIZE 64
//数据接收结构体
struct msg_rv
{
	int mtype;
	char msg[50];
	
};

//数据发送结构体
struct msg_snd
{
	int mtype;
	char msg[50];
};

int main(void)
{
	int msgid;
	struct msg_rv data;
	struct msg_snd snddata;
	char buff[50];
	
	data.mtype = 89;
	snddata.mtype = 88;
		
	//获取msgid
	msgid = msgget((key_t)123,IPC_CREAT|0666);
	if(msgid == -1)	{
		printf("msgid failed!\n");
		return -1;
	}
	
	while(1) {	
		//接受
		if(msgrcv(msgid,(void *)&data,sizeof(struct msg_rv),data.mtype,0)==-1)
		{
			printf("msgsnd failed!\n");
			return -1;
		}
		printf("receive data:%s\n",data.msg);
		if(strncmp(data.msg,"end",3)==0) {
			break;
		}
		
		//发送
		printf("please input data:\n");
		bzero(buff,50);
		fgets(buff,50,stdin);
		strcpy(snddata.msg,buff);
		printf("data = %s\n",snddata.msg);
		if(strncmp(snddata.msg,"end",3)==0) {
			break;
		}
		msgsnd(msgid,(void *)&snddata,strlen(buff)+4,0);//得到的值发送出去
		printf("run here!\n");	
	}
	
	//撤消消息队列
	msgctl(msgid,IPC_RMID,0);
	return 0;
}
ログイン後にコピー

2.1.6 信号量

信号量协调不同进程对于共享资源的访问,它是不同进程间或一个给定进程内部不同线程间同步的机制。

1Linuxは複数のプロセスを作成できますか?

1、信号量概述
(1)二值信号量
值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0
(2)计数信号量
值在0到n之间。用来统计资源,其值代表可用资源数
(3)对信号量的操作
P操作:即申请资源,亦即将信号量值减1,可能引起进程睡眠。
V操作:即释放资源,亦即将信号量值加1,V操作从不会睡眠。
等0操作:不申请也不释放资源,而是令进程阻塞直到信号量的值为0为止

2、信号量相关的接口函数
(1) 创建信号量集合semget()

#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
    参数:key ----》键值
          nsems----》你创建的信号量集中信号量的个数
          semflg----》 IPC_CREAT|0666组合
	返回值:成功—信号量ID
			出错—-1
ログイン後にコピー

(2)设置/删除信号量集 semctl()

   #include <sys/sem.h>
      int semctl(int semid, int semnum, int cmd, ...);
	  返回值:成功—0 	失败—-1
ログイン後にコピー

1Linuxは複数のプロセスを作成できますか?

(3)信号量的PV操作 semop()
核心:信号量为 <=0 时进行p操作,会阻塞程序,直到另一进程中是该信号进行了v操作后,本程序才会继续运行------》key值相同,信号量共通
p 减一操作
v 加一操作

  #include <sys/sem.h>
  int semop(int semid, struct sembuf *sops, size_t nsops);
  返回值:成功—0   出错—-1
  参数:semid ----》semget的返回值
        nsops ---》要操作的信号量的个数(结构体的个数)
  		sops---》信号量操作结构体
		struct sembuf {
			short  sem_num	;=>> 要操作的信号量的编号(数组下标)
			short  sem_op;   =>> 0 :  等待,直到信号量的值变成0
                            	1 :  释放资源,V操作
                           		-1 :  分配资源,P操作                    
			short  sem_flg;   =>> 0/IPC_NOWAIT/SEM_UNDO
								SEM_UNDO: 程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值;
								IPC_NOWAIT: 对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息;
		};
ログイン後にコピー

3、信号量协同共享内存示例代码

pthread1.c  :-----------》

int main()
{
	int semid;
	int shmid;
	char *p;
	struct sembuf mysembuf1,mysembuf2;
	
	mysembuf1.sem_num = 0;
	mysembuf1.sem_flg = SEM_UNDO;
	mysembuf1.sem_op = 1;
	
	mysembuf2.sem_num = 1;
	mysembuf2.sem_flg = SEM_UNDO;
	mysembuf2.sem_op = -1;
	
	// 创建信号量集合
	semid = semget((key_t)789,2,IPC_CREAT|0666);
	if(semid == -1)	{
		perror("creat sem failed!\n");
		return -1;
	}
	// 创建共享内存
	shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
	if((shmid == -1)&&(errno == EEXIST)) {
		shmid = shmget((key_t)456,1024,0666);
	}
	// 映射共享内存到进程
	p = (char *)shmat(shmid,NULL,0);

	while(1) {
		semop(semid,&mysembuf2,1); // 对信号量2进行p操作(减一)
		printf("the message I recv is:%s\n",p);
		printf("please input a message!\n");
		scanf("%s",p);
		printf("message is %s\n",p);
		semop(semid,&mysembuf1,1); // 对信号量1进行v操作(加一)
	}
	
	//解除映射 
	shmdt(p);
	
	//删除共享内存
	shmctl(semid, IPC_RMID, NULL); 
}

pthread2.c  :-----------》
int main() {
	int semid;
	int shmid;
	char *p;
	struct sembuf mysembuf1,mysembuf2;
	
	mysembuf1.sem_num = 0;  // 信号集合中的第一个信号
	mysembuf1.sem_flg = SEM_UNDO;
	mysembuf1.sem_op = -1;   //p操作

	mysembuf2.sem_num = 1;  // 信号集合中的第二个信号
	mysembuf2.sem_flg = SEM_UNDO;
	mysembuf2.sem_op = 1;    // v操作
	// 创建信号量集合
	semid = semget((key_t)789,2,IPC_CREAT|0666);
	if(semid == -1)	{
		perror("creat sem failed!\n");
		return -1;
	}
	// 设置信号量的值
	semctl(semid,0,SETVAL,1);  //第一个信号量初值为1
	printf("sem num is:%d\n",semctl(semid,0,GETVAL));
	semctl(semid,1,SETVAL,0);  //第二个信号量初值为0
	printf("sem num is:%d\n",semctl(semid,1,GETVAL));
	// 创建共享内存
	shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
	if((shmid == -1)&&(errno == EEXIST)) {
		shmid = shmget((key_t)456,1024,0666);
	}
	// 映射共享内存到进程
	p = (char *)shmat(shmid,NULL,0);
	while(1) {
		semop(semid,&mysembuf1,1); // 对信号量1进行p操作(减一)不阻塞,因为初值为1
		  // 执行完这句话以后信号量的值就立马变成1
		printf("the message I recv is:%s\n",p); 
		printf("please input a message!\n");
		scanf("%s",p);
		printf("message is %s\n",p);
		semop(semid,&mysembuf2,1); // 对信号量2进行v操作(加一)不阻塞,因为初值为0	
	}
	
	//解除映射 
	shmdt(p);
	
	//删除共享内存
	shmctl(semid, IPC_RMID, NULL);
}
ログイン後にコピー

2.3 IPC shell命令操作

  • ipcs -q 查看消息队列
  • ipcrm -q MSG_ID 删除消息队列
  • ipcs -m 查看共享内存
  • ipcrm -m SHM_ID 删除共享内存
  • ipcs -s 查看信号量
  • ipcrm -s SEM_ID 删除信号量

2.2 进程间通讯方式比较

  • pipe: 具有亲缘关系的进程间,单工,数据在内存中
  • fifo: 可用于任意进程间,双工,有文件名,数据在内存
  • signal: 唯一的异步通信方式
  • msg:常用于cs模式中, 按消息类型访问 ,可有优先级
  • shm:效率最高(直接访问内存) ,需要同步、互斥机制
  • sem:配合共享内存使用,用以实现同步和互斥

推荐学习:Linux视频教程

以上がLinuxは複数のプロセスを作成できますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート