Linux多進程編程實例


前言:編寫多進程程序時,我們應該了解一下,創建一個子進程時,操作系統內核是怎樣做的。當通過fork函數創建新的子進程時,內核將父進程的用戶地址空間的內容復制給子進程,這樣父子進程擁有各自獨立的用戶空間,當父進程修該變量的值時不會影響子進程中的相應變量。但為了提高效率,Linux采用了COW(copy on write)算法,子進程創建時,父子進程享有相同的地址空間,只是在頁表中設置cow標識,只有在父進程或子進程執行寫數據操作時,才為子進程申請一個物理頁,將父進程空間中相應數據所在頁的內容復制到該物理頁,然后將該頁映射到子進程用戶地址空間的適當位置。此外,子進程還繼承父進程的其他資源,例如父進程打開的文件描述符和工作目錄等(因為子進程能繼承父進程的文件描述符,所以實現進程之間通信時可以采用管道通信方式)。 子進程和父進程的代碼區,以及初始數據是一模一樣的,只是fork函數返回的pid在父子進程中有不同的值,所以根據pid的分支語句父子進程會執行不同的結果。如果我們只是想用子進程完成某一部分的功能,當功能完成后我們應該立即使用exit或者return函數結束子進程,不然,若子進程的創建是在一個循環中,並且沒有使用exit或者return推出,那么子進程,會像父進程一樣執行循環中的代碼,然后子進程又創建子進程,無限循環,具體例子額看后面的一個服務器端代碼。

 進程創建與退出

相關api

pid_t fork()                             //#include <unistd.h> 創建進程

void exit(int status)      //#include <stdlib.h>  退出進程,退出時會調用atexit注冊的函數,先注冊的后調用,exit函數還會按需調用fclose函數關閉打開的文件流

int atexit(void(*func)(void))    //#include <stdlib.h>  為進程注冊退出時調用的函數

void _exit(int status)         //#include <unistd.h>   直接退出進程

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int glob=10;
static void my_exit1(void)  //進程退出時調用函數
{
    printf("pid=%d first exit handler\n",getpid());
}

static void my_exit2(void)
{
    printf("pid=%d second exit handler\n",getpid());
}

int main()
{
    int local;
    pid_t pid;
    local=8;
    if(atexit(my_exit1)!=0)  //為進程注冊的退出時調用函數也會被子進程共享,先注冊的后調用
    {
        perror("atexit");
    }

    if(atexit(my_exit2)!=0)
    {
        perror("atexit");
    }

    if((pid=fork())==0)
    {
        printf("child pid is %d\n",getpid());   //子進程執行某個任務完后盡量使用exit退出,不然,若父進程中創建的子進程位於循環中,可能會引起未知的行為
    }
    else if(pid>0)
    {
        sleep(10);
        glob++;
        local--;
        printf("father pid is %d\n",getpid());       
    }
    else
    {
        perror("fork");
    }
    printf("pid=%d,glob=%d,localar=%d\n",getpid(),glob,local);//這段代碼父子進程共享
    return 0;//也可以使用exit(0)
}

加載可執行文件映像

#include <unistd.h>

int execl(const char *path,const char *arg,...);                     //  l表示命令行參數為以0結束的多個字符串組成 ,v表示命令行參數為以0結束的字符串數組組成

int execle(const char *path,const char *arg,...,char *const envp[]);  //e表示指定環境表量,原來的環境變量不起作用

int execlp(const char *file,const char *arg,...);                      //p表示可執行映像文件在環境變量path路徑中查找

int execv(cosnt char *path,char *const argv[]);

int execve(const char *path,char *const argv[],char *const envp[]);

int execvp(const char *file,char *const argv[]);

path 代表可執行文件路徑,arg代表命令行參數

//testexec.c 被調用程序
#include <stdio.h> int glob=18; extern char **environ; int main(int argc,char *argv[]) { int local=20; int k; char **ptr=environ; glob++; local++; printf("&glob=%x,&local=%x\n",&glob,&local); //打印變量的地址 printf("argc=%d\n",argc); for(k=0;k<argc;++k) { printf("argv[%d]\t %s\n",k,argv[k]); //打印命令行參數 } for(ptr=environ;*ptr!=0;++ptr) { printf("%s\n",*ptr); //打印環境變量 } return 0; }
//useexec.c  
#include <stdio.h> #include <unistd.h> int main() { char *nenv[]={"NAME=value","NEXT=nextvale",(char*)0}; char *nargv[]={"testexec","param1","param2",(char *)0}; //命令行參數都以0結尾 pid_t pid; pid=fork(); switch(pid) { case 0: execve("./testexec",nargv,nenv); //指定環境變量,原來的環境變量不起作用 //execl("./testexec","testexec",0); //不指定環境表量 perror("exec"); exit(1); case -1: perror("fork"); exit(1); default: wait(NULL); printf("exec is completed\n"); exit(0); } }

等待子進程結束

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int * status)                  //暫停執行,直到一個子進程結束,成功返回進程pid,否則返回-1

pid_t waitpid(pid_t pid,int *status,int options)     //等待指定子進程結束,options指定等待方式

返回值:若設置了WNOHANG且未發現子進程則返回0,出錯則返回-1

pid的意義

pid<-1     等待pid所代表的進程組中的進程

pid=-1     等待任何子進程

pid=0      等待與該進程同組的進程

pid>0      等待的進程標識

options的意義

WNOHANG      //表示不阻塞

WUNTRACED   //當有子進程結束時返回

//一個回聲服務器服務端例子 tcpserver.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <sys/wait.h> #define PORT 4008 #define BACKLOG 10 #define BUFSIZE 4096 //void process(char * cmd); int main(int argc,char* argv[]) { int lsockfd,rsockfd; struct sockaddr_in lsocket,rsocket; if((lsockfd=socket(AF_INET,SOCK_STREAM,0))<0) { perror("socket"); exit(1); } lsocket.sin_family=AF_INET; lsocket.sin_port=htons(PORT); lsocket.sin_addr.s_addr=INADDR_ANY; bzero(&(lsocket.sin_zero),8); if(bind(lsockfd,(struct sockaddr *)&lsocket,sizeof(struct sockaddr))<0) { perror("bind"); exit(1); } if(listen(lsockfd,BACKLOG)<0) { perror("listen"); exit(1); } int sin_size=sizeof(struct sockaddr); int count=0; while(1) { printf("wait for connecting!\n"); if((rsockfd=accept(lsockfd,(struct sockaddr *)&rsocket,&sin_size))<0) { perror("accept"); continue; } count++; printf("someone connect!,current people %d\n",count); if(!fork()) { char str[BUFSIZE]; int numbytes=0; while(1) { if((numbytes=recv(rsockfd,str,BUFSIZE-1,0))<0) { perror("recv"); break; } str[numbytes]='\0'; if(strcmp(str,"quit")==0) { printf("client quit!\n"); break; } printf("receive a message: %s\n",str); if(send(rsockfd,str,strlen(str),0)<0) { perror("send"); break; } } close(rsockfd); exit(0); } while(waitpid(-1,NULL,WNOHANG)>0) //此處不會阻塞若第三個參數為WUNTRACED則會阻塞 { count--; printf("someone quit!,current people have %d\n",count); } } return 0; }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM