首頁 > 運維 > linux運維 > 主體

linux可不可以建立多個進程

王林
發布: 2023-05-20 18:32:46
轉載
1358 人瀏覽過

linux可以建立多個進程。 Linux可以同時處理多個任務,支援多進程,以實現系統資源的最大化利用。 linux進程間的通訊方式:1、利用無名管道pipe;2、利用有名管道(FIFO);3、利用訊號single;4、利用共享記憶體;5、利用訊息佇列;6、利用訊號量。

linux可以建立多個進程。

linux 支援多進程。多進程系統的一個好處,就是可以同時處理多個任務,實現系統資源的最大利用。

第1章linux多進程介紹

1.1 概述

1.1.1 進程概念

# linux中把正在運作的程式稱為進程。
程式:靜態的概念,它是一個編譯好的二進位檔案
進程:動態的概念,當程式運行的時候,系統會自動執行一個對應進程
進程包含了進程控制塊(PCB),程式碼段,資料段三個部分
進程控制塊:在linux中是用一個結構體來表示的,記錄進程的狀態資訊
殭屍進程:父進程優先於子進程退出
如果你建立了子進程,但是在父進程中沒有回收該子進程的資源,那麼該子進程就會變成殭屍行程,殭屍行程最終會由系統中一個叫做INIT的行程回收。
init進程(1號進程)是系統啟動的時候運行的第一個進程,是所有進程的祖進程。

1.1.2 行程查看shell指令

  • #top 查看動態的進程資訊

  • ps -ef 查看進程的詳細資訊

  • pstree 以樹狀的形式顯示進程的資訊

  • bg 將掛起的進程放到後台執行

1.2 進程運行狀態

1.2.1 運行狀態

  • 執行態(RUNNING):進程正在佔有CPU。

  • 就緒態( RUNNING):進程處於等待佇列中等待調度。

  • 淺睡眠( INTERRUPTABLE):此時進程在等待一個事件的發生或某種系統資源,可回應訊號。

  • 深度睡眠( UNINTERRUPTABLE): 此時進程在等待一個事件的發生或某種系統資源, 無法回應訊號。

  • 停止態( STOPPED): 此時進程被暫停。

  • 殭屍態( ZOMBIE): 此時行程不能被調度,但PCB未被釋放。

  • 死亡態( DEAD): 這是一個已終止的進程,且PCB將會被釋放

linux可不可以建立多個進程

1.2.2 使用者狀態/核心狀態

核心態:也叫核心空間,是核心行程/執行緒所在的區域。主要負責運作系統、硬體互動。
使用者狀態:也叫使用者空間,是使用者行程/執行緒所在的區域。主要用於執行使用者程式。

linux可不可以建立多個進程

1、區別
核心狀態:執行的程式碼不受任何限制,CPU可以執行任何指令。
使用者狀態:不能調度CPU,不能直接存取硬體。運行的程式碼需要受到CPU的許多檢查,不能直接存取核心資料和程序,也就是不可以像核心態執行緒一樣存取任何有效位址。

作業系統在執行使用者程式時,主要工作在使用者狀態,只有在執行沒有權限完成的任務時才會切換到核心態。

2、區分使用者狀態和核心態原因

  • #保護機制,防止使用者程式誤操作或惡意破壞系統

  • 保證資源的集中管理,減少資源的使用衝突。

3、用戶態切換到核心態方式
(1)系統呼叫(主動)
系統呼叫( system call)是作業系統提供給使用者程序請求作業系統做一些特權操作的接口,也就是為使用者程序提供服務的視窗。在Linux下可以透過man syscalls指令查看Linux提供的所有系統呼叫API介面。
由於使用者態無法完成某些任務,使用者態會要求切換到核心態,核心態透過為使用者專門開放的中斷完成切換。

(2)週邊設備中斷(被動)
週邊設備發出中斷訊號,當中斷發生後,目前運行的進程暫停運行,並由作業系統核心對中斷進程處理,如果中斷之前CPU執行的是用戶態程序,就相當於從用戶態向核心態的切換。
中斷用來確保CPU控制權交給作業系統,讓作業系統可以執行某些操作。

(3)異常(被動)
在執行用戶程序時出現某些不可知的異常,會從用戶程序切換到內核中處理該異常的程序,也就是切換到了內核態。

1.3 進程介面函數

1.3.1 進程建立fork/vfork

1、fork()、vfork()
(1 )新建的進程稱為子進程,它複製了父進程的所有資源(只在創建的那個時間複製一次,以後全局變數值是不同),父子進程誰先誰後不確定。

#include 
pid_t fork(void);
  		返回值:  > 0表示处于父进程中 这个时候的返回值就是**子进程进程id号**
                  ==0 表示处于子进程中
                  < 0 创建进程出错
                  
#include 
  		 #include 
		pid_t vfork(void);
登入後複製

(2)**vfork()**子程序共享了父進程的所有資源,它一定是子程序先運行,然後才是父進程運行(即使你加上sleep()人為去幹擾也是沒有用的)
(3)注意
子進程中使用了exit()跟沒有使用結果完全不一樣
父子進程中是否使用sleep()來讓出cpu時間片也是不一樣的
父行程中是否使用wait(),waitpid()結果也是不一樣的

linux可不可以建立多個進程

##(4)行程切換執行


linux可不可以建立多個進程

1.3.2 進程的退出exit/_exit

1、exit()、_exit()

    #include 
    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 
    pid_t wait(int *stat_loc);
   返回值:你回收的那个子进程的id号
   参数:stat_loc --》保存子进程退出时的状态信息(不仅仅只是返回值)
  		 stat_loc里面不仅仅只是保存了exit退出时的数值,它还保存了子进程退出时是哪个信号让它退出的,		
  	    出错了是什么原因导致的。
登入後複製

linux可不可以建立多個進程

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,且没有已退出的子进程
登入後複製

1.3.4 取得進程的id–getpid

(1)获取自己的id    getpid()
		#include 
		pid_t getpid(void);  返回值:就是该进程的id号
(2) 获取父进程id    getppid()
         #include 
         pid_t getppid(void);  返回值:就是父进程的id号
登入後複製

第2章linux多進程間通訊方式

不管是進程間的通信,或是線程間的通信。這句話可以重寫為:解決共享資源的分配問題是其根本目的,即協調多個進程/線程對共享資源的存取

2.1 進程間的通訊方式

1、傳統的進程間通訊方式

  • #無名管道

  • 有名管道

  • 訊號

2、System V IPC物件

  • #共享記憶體

  • ##訊息佇列
  • 信號量
  • 3、BSD

    網路套接字(socket)
  1. 2.1.1 無名管道pipe

1、特點:最原始的進程間的通訊方式

它只能在具有

親緣關係的進程間通訊
(父子程序,兄弟流程); 它沒有名字(是存在的); 可以在linux和windows之間的共享中建立(根本就不會產生管道檔案),但是有名管道就不可以了(產生管道文件);
半雙工通訊。

2、無名管道的使用

(1)建立pipe()

    #include 
   int pipe(int fildes[2]);
   参数:fildes[2]里面放的是两个文件描述符fildes[0],fildes[1]
         fildes[0] 读端
         fildes[1] 写端
   返回值:成功返回0    失败返回-1
登入後複製

(2)pipe訊息收發

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、特點:隨便兩個行程之間都行

    不能在linux和windows之間的共享中建立;
  • #保證寫入的原子性(原子性:要嘛不做,要做就一口氣做完不受外界的干擾);
  • 有名管道
  • 不能夠覆蓋著創建

    (一般程式碼中使用access()函數來判斷是否存在,如果已經存在同名的管道,就不能再創建);

    ##使用完畢記得關閉;
  • 當管道以
  • 只讀
  • 的方式打開,會阻塞,直到有另外一個程序以

    只寫的方式打開這個管道,那麼就不阻塞了;如果是以可讀寫的方式打開,就不會阻塞了。

    全雙工通信,半雙道。
  • 2、有名管道的使用
  • (1)建立mkfifo()
   #include 
   #include 
   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 訊號single

程式(進程)在運作過程中,外界不定時會發出訊號給該程序,而這個時候程式面臨兩種選擇:

不理它(阻塞/忽略)

阻塞:是指將訊號掛起,等到程式運行完了再去回應

忽略:捨棄這個訊號
回應它

1、linux當中有哪些訊號:kill -l 檢視

 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 
 int kill(pid_t pid, int sig);
 参数:
   pid ----》进程的id
			正数:要接收信号的进程的进程号
			0:信号被发送到所有和pid进程在同一个进程组的进程
			-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
   sig ----》信号名字
   返回值:0 成功
		   -1 出错
登入後複製

(2)訊號的捕捉signal()

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

(3)等待訊號pause()

#include 
int pause(void);
	返回值:-1 把error值设为EINTR
			0 成功
登入後複製

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

#include 

  		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 
 	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 
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  
int shmget(key_t key, size_t size, int shmflg);
返回值:成功—共享内存对象的mid(标识符) 出错—-1
参数:key----》创建共享内存需要用到的键值
      size----》内存空间的大小(字节)
      shmflg----》设置属性  IPC_CREAT   IPC_EXCL    0666组合
      
key键值的获取有两种方法:
 **方法一**:使用ftok()生成键值
            #include 
            #include 
            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 
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 
int shmdt(const void *shmaddr);
参数:shmaddr----》 shmat()共享内存映射后的地址
返回值:成功—0 	出错—-1
登入後複製

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

 #include 
 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 
 int msgget(key_t key, int msgflg);
登入後複製

linux可不可以建立多個進程

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

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

linux可不可以建立多個進程

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

#include 
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
登入後複製

linux可不可以建立多個進程

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 信号量

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

linux可不可以建立多個進程

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

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

#include 
int semget(key_t key, int nsems, int semflg);
    参数:key ----》键值
          nsems----》你创建的信号量集中信号量的个数
          semflg----》 IPC_CREAT|0666组合
	返回值:成功—信号量ID
			出错—-1
登入後複製

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

   #include 
      int semctl(int semid, int semnum, int cmd, ...);
	  返回值:成功—0 	失败—-1
登入後複製

linux可不可以建立多個進程

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

  #include 
  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可不可以建立多個進程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!