C 多進程

參考:https://blog.csdn.net/wucz122140729/article/details/105113379編程

在liunx中一些與進程相關的命令:服務器

ps 查看當前終端的進程數據結構

ps -ef 查看系統的所有進程多線程

ps -ef |grep test查看系統的所有進程而且包含test字符的記錄socket

在上面的查詢結果中各個字段的意義:UID :啓動進程的操做系統用戶。PID :進程編號。PPID :進程的父進程的編號。C :CPU使用的資源百分比。STIME :進程啓動時間。CMD :執行的是什麼指令。函數

 

 

在C語言中用getpid()庫函數來獲取當前程序運行時的進程編號。操作系統

如:.net

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>線程

int main()
{
printf("本程序的進程編號是:%d\n",getpid());
}blog

 

在C的代碼中開啓新的進程,進行多進程編程,主要是使用fork()函數,這個函數執行後會開啓一個新的子進程,該子進程會複製本進程在執行fork()函數前的全部數據。調用fork()函數後,後面的代碼就會有兩個進程分別來執行,就是說後面的代碼會被執行兩次,彼此之間互不干擾。

fork()函數的返回值,是一個整數。在父進程中返回值是子進程的編號,在子進程中返回值是0。因此在後面運行的代碼中就能夠經過判斷fork()函數的返回值來判斷這次運行是父進程仍是子進程。

如:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
printf("本程序的進程編號是:%d\n",getpid());

int ipid=fork();

printf("pid=%d\n",ipid);

if (ipid!=0) printf("父進程編號是:%d\n",getpid());
else printf("子進程編號是:%d\n",getpid());

}

 

多進程的應用。咱們能夠利用多線程將以前的那個socket簡單的聊天通信作成多線程的,便可以容許多個客戶端來鏈接個人服務器端。客戶端的代碼不用修改,主要是修改服務器端的代碼。

基本思想是,咱們利用主線程來專門監聽來鏈接的客戶端,當監聽到有一個客戶端鏈接上來後就開啓一個子線程來與該鏈接上來的客戶端進行通信。主線程則繼續監聽客戶端的鏈接。這樣就爲每個鏈接上來的客戶端開闢了一個子線程來進行通訊,就不會出現線程的堵塞。

服務器端加上多線程後的代碼:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>



int main()
{

  // signal(SIGCHLD,SIG_IGN);  // 忽略子進程退出的信號,避免產生殭屍進程

  // 第1步:建立服務端的socket。
  int listenfd = socket(AF_INET,SOCK_STREAM,0);  

  // 第2步:把服務端用於通訊的地址和端口綁定到socket上。
  struct sockaddr_in servaddr;    // 服務端地址信息的數據結構。
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // 協議族,在socket編程中只能是AF_INET。
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // 任意ip地址。
  //servaddr.sin_addr.s_addr = inet_addr("118.89.50.198"); // 指定ip地址。
  servaddr.sin_port = htons(5051);  // 指定通訊端口。
  if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { perror("bind"); close(listenfd); return -1; }

  // 第3步:把socket設置爲監聽模式。
  if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }
  // 第4步:接受客戶端的鏈接。

  while(1){
     int  clientfd;                  // 客戶端的socket。
     int  socklen=sizeof(struct sockaddr_in); // struct sockaddr_in的大小
     struct sockaddr_in clientaddr;  // 客戶端的地址信息。
     clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);
     printf("客戶端(%s)已鏈接。\n",inet_ntoa(clientaddr.sin_addr));

     if(fork()>0) {continue;close(clientfd); }  // 父進程回到while,繼續Accept。由於父進程不須要用clientfd與客戶端進行通訊
     
     close(listenfd);//這裏關閉掉監聽的socket,由於子進程會複製父進程中全部的數據,而監聽的socket再子進程中是沒有用的,因此這裏關閉掉釋放資源
      // 第5步:與客戶端通訊,接收客戶端發過來的報文後,回覆ok。
     char buffer[1024];
     while (1)
     {
       memset(buffer,0,sizeof(buffer));
       if (recv(clientfd,buffer,sizeof(buffer),0)<=0) break;   // 接收客戶端的請求報文。
       printf("接收:%s\n",buffer);
       memset(buffer,0,sizeof(buffer));

       printf("發送:");
       scanf("%s",buffer);
       if (send(clientfd,buffer,strlen(buffer),0)<=0) break;   // 向客戶端發送響應結果。
     }
      printf("客戶端已斷開鏈接。\n");

      return 0;  // 或者exit(0),子進程退出。
     }
}
加上signal(SIGCHLD,SIG_IGN);  這一句的做用是爲了清理掉殭屍進程,殭屍進程就是子進程再return或者exit以後,若是父進程還在執行。其實是沒有徹底將子進程所佔用的資源清理掉的。加上這句代碼後能夠清除掉這些殭屍進程。
相關文章
相關標籤/搜索