打算开始写apue的系列内容,及时更新


前言

从处理方式来分类的话,上一节中讲到的网络sokcet通讯属于迭代服务器,该服务器每次只能处理一个客户的请求,实现简单,但是效率很低下,今天要讲的是多进程编程,属于并发服务器,可以同时处理多个客户的请求,相较而言,效率很高但是实现复杂。


在学习多进程编程之前我们首先需要知道的是一下内容

一、fork()函数

在Linux下有两个基本的系统调用可以用于创建子进程:fork()和vfork(),当一个进程正在运行的时候,使用了fork()函数之后就会创建另一个进程。与一般函数不同的是,fork()函数会有两次返回值,一次返回给父进程(该返回值是子进程的PID(Process ID)),第二次返回是给子进程,其返回值为0.所以在调用该函数以后,我们需要通过返回值来判断当前的代码时父进程还是子进程在运行:

  • 返回值大于0 -> 父进程在运行
  • 返回值等于0 -> 子进程在运行
  • 返回值小于0 -> 函数系统调用出错

通常系统调用出错的原因有两个:①已存在的系统进程已经太多;②该实际用户ID的进程总数已经超过了限制。

一个简单的例子

代码如下(示例):

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc,char **argv)
{
        pid_t  pid;

        printf("parent process PID[%d] start running!!!\n",getpid());

        pid = fork();

        if(pid < 0)
        {
                printf("\n\n");
                printf("fork() create child process failure: %s\n",strerror(errno));
                return -1;
        }
        else if(pid == 0)
        {
                printf("\n");
                printf("child process PID[ %d ] start running,my parent PID is [ %d ]\n",getpid(),getppid());
                return 0;
        }
        else    //if(pid > 0)
        {
                printf("\n");
                sleep(2);
                printf("parent process PID[%d] continue running,and child process PID is [ %d ]\n",getpid(),pid);
                printf("\n");
                return 0;
        }
}

其运行结果如图所示:

在这里插入图片描述
该示例代码中,使用了fork()函数了之后,下面通过pid的返回值来判断是父进程还是子进程在被调用,或者是系统调用失败(pid < 0 时)。其父子进程的运行顺序并不是一定的,这个系统的调度策略有关系。

2.子进程是父进程的一个副本

当fork()系统调用时,创建了一个新的子进程,这个子进程时父进程的一个副本,系统在创建了这个新的子进程了之后,会将父进程的文本段、数据段、堆栈都复制一份给子进程,子进程拥有自己独立的空间,对自己内存的修改并不会影响父进程相应的内存空间

代码如下(示例):

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

int g_var = 6;
char g_buf[] = "a string write to stdout\n";

int main(int argc,char **argv)
{
        int var = 88;
        pid_t   pid;

        if( write(STDOUT_FILENO,g_buf,sizeof(g_buf) -1) < 0)
        {
                printf("\n\n");
                printf("write string to stdout error: %s\n",strerror(errno));
                return -1;
        }

        printf("before fork\n");

        if( (pid = fork()) < 0 )
        {
                printf("\n");
                printf("fork() error: %s\n",strerror(errno));
                return -2;
        }
        else if(0 == pid)
        {
                printf("\n");
                printf("child process PID[%d] running!!!\n",getpid());
                g_var++;
                var++;
        }
        else
        {
                printf("\n");
                printf("parent process PID[%d] waiting!!!\n",getpid());
                sleep(1);
        }

        printf("PID = %ld,g_var = %d,var = %d\n",(long)getpid(),g_var,var);
        return 0;
}

其运行结果如图所示:

这里添加图片描述由运行结果可知,当在子进程对全局变量和局部变量进行自加的时候,并不会影响到父进程中全局变量和局部变量的值。在该代码中,当pid>0,也就是执行父进程部分代码的时候,使用了休眠函数sleep(),在这里的目的是想让子进程先执行,但是这并不一定能够奏效,当系统负载较大的时候一秒的时间内操作系统可以没调度到子进程运行。


总结

这篇文章只是多进程编程的一个小开始,真正在多进程编程中也会用到fork()函数,它的使用思路是:每来一个客户端向服务器端连接的时候,就创建一个子进程为客户端进行服务。后续会继续更新哒。

Logo

华为云1024程序员节送福利,参与活动赢单人4000元礼包,更有热门技术干货免费学习

更多推荐