fork函数与多进程并发访问服务器

目录

  • 子进程与父进程
  • fork()函数
  • 多进程服务器

子进程与父进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。子进程指的是由另一进程(对应称之为父进程)所创建的进程
子进程通常通过父进程调用fork()函数产生,调用fork()后,操作系统会复制一个与父进程几乎完全相同的子进程,这两个进程共享代码空间(即文本段),但是数据空间是互相独立的

fork()函数

函数原型及所需的头文件如下

#include <unistd.h>
pid_t fork(void);

fork意为分叉,fork()函数用来创建一个子进程,并返回pid_t类型的进程ID

fork()函数调用时不需要参数,但是会返回两个返回值分别返回给父进程和子进程,父进程返回子进程的进程ID,子进程返回0,fork()函数执行后可以通过判断返回值判断父子进程,每个进程也可以通过调用getpid()函数获得自己的进程号,也可以通过getppid()获得自己父进程的进程号

值得注意的是,子进程拥有父进程当前运行到的位置(两进程的程序计数器PC值相同,即子进程是从fork返回处开始执行的),不过fork()创建进程之后子进程与父进程谁先开始运行是完全随机的,可以通过进程间通信(IPC)解决父子进程的同步问题

创建子进程后,父进程需要等待子进程的退出并为其善后:

如果父进程在子进程退出之前退出了,这时候子进程就变成了孤儿进程。当然每一个进程都应该有一个独一无_的父进程,init进程就是这样的-个“慈父”,Linux内核中所有的子进程在变成孤儿进程之后都会被init进程"领养”,这也意味着孤儿进程的父进程最终会变成init进程;如果一个已经终止但其父进程尚未对其进行善后处理(获取终止子进程的有关信息如CPU时间片、释放它锁占用的资源如文件描述符等)的进程被称僵死进程(zombie),ps命令将僵死进程的状态打印为Z

。那么父进程如何为子进程善后呢?

父进程通常通过调用wait()函数为子进程善后
wait()与waitpid()

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

当一个进程正常或异常退出时,内核就会向其父进程发送SIGCHLD信号。因为子进程退出是一个异步事件,所以这种信号也是内核向父进程发送的一个异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即将被执行的函数,父进程可以调用wait()或waitpid()可以用来查看子进程退出的状态

在一个子进程终止前,wait使其调用者阻塞,而waitpid有一选项可使调用者不用阻塞。
waitpid并不等待在其调用的之后的第一个终止进程,他有若干个选项,可以控制他所等待的进程

多进程服务器

多进程并发访问服务器的原理图如下,相较于多线程与多路复用,多进程是比较简单的一种实现服务器并发访问的方式,不过缺点也很明显:独立的地址空间使用进程共享状态信息变得困难,为了共享信息,必须使用IPC(进程间通信机制)。多进程的另一个缺点是,它们往往比较慢,因为进程控制和IPC的开销都较高
在这里插入图片描述
多进程服务器的模型非常清晰,一个进程不可能覆盖另一个进程的用户地址空间。这就消除了许多令人迷惑的错误。这是多进程实现并发服务器的优点。下面给出多进程服务器的实现代码(省略了socket部分,不过socket部分需要调用setsockopt()函数避免端口占用)

while(1)
  {
      printf("\n%d start waiting and accepting new client connect... \n",sockfd);
      client_fd=accept(sockfd,(struct sockaddr *)&cli_addr,&cliaddr_len);
      if(client_fd<0)
      {
          printf("connect failure: %s\n",strerror(errno));
          return -3;  
      }
      printf("accept new client with fd \n");

      pid=fork();
      if(pid<0)
      {
          printf("create new process failure: %s\n",strerror(errno));
          close(client_fd);
          continue;
      }
      if(pid>0)
      {
          close(client_fd);
          continue;
      }
      if(pid==0)
      {
          close(sockfd);
          memset(buf,0,sizeof(buf)); 
          rv=read(client_fd,buf,sizeof(buf));
          if(rv<0)
          {
              printf("connect to client error: %s\n",strerror(errno));
              close(client_fd);
              exit(0);//如果出错子进程直接退出
          }
          else if(rv==0)
          {
              printf("client connect to server get disconnected \n");
              close(client_fd);
              exit(0);
          }
          printf("get %d Bytes from client \n",rv);
      
          rv=write(client_fd,MSG_STR,strlen(MSG_STR));
          if(rv<0)
          {
              printf("write %d Bytes to client error: %s\n",rv,strerror(errno));
              close(client_fd);
              exit(0);
          }
          printf("write %d Bytes to client successfully! \n",rv);
          sleep(1);
          close(client_fd);
          exit(0);
      }   
  }

如果不等待子进程退出则需安装信号

// 避免出现僵尸进程
signal(SIGCHLD, SIG_IGN);

signal()函数会在捕捉到SIGCHLD信号后对其进行忽略操作(SIG_IGN,IGN即为ignore的缩写)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值