Linux多进程编程实例(转载)

转载地址:https://www.cnblogs.com/CodingUniversal/p/7396671.html

前言:编写多进程程序时,我们应该了解一下,创建一个子进程时,操作系统内核是怎样做的。当通过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)
}

加载可执行文件映像

扫描二维码关注公众号,回复: 2826989 查看本文章

#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;
}

猜你喜欢

转载自blog.csdn.net/liuxu324/article/details/81108574
今日推荐