示例项目

分支进程

操作系统的主要职责之一是管理在操作系统中运行的进程。在Linux操作系统中,Linux内核监督启动和管理进程。在Linux中,运行的每个进程都是由其他进程启动的。启动其他程序的程序的主要示例是shell程序。当您在Linux中打开终端并键入命令时,您正在与shell程序进行交互。shell程序用户执行的一个常见任务是启动程序。要从shell中启动一个程序,我们在shell程序中输入一个命令行:shell程序读取我们的命令并管理启动我们要求它启动的程序的过程。Shell程序并不是Linux中唯一可以启动另一个进程的程序。实际上,Linux中的任何程序都可以通过在Linux内核中创建一个适当的系统来启动其他程序。

在本例和下一个示例中,我们将了解用于在Linux中创建进程的两种常用技术。在这个示例中,我们将看到Linux中的进程如何通过fork克隆自己来启动子进程。在下一个示例中,我们将看到Linux中的程序如何启动另一个不同的程序。

分支服务器

在我们的web服务器示例中,我们看到了服务器赢博体育程序可以使用线程同时与多个客户端交互的几种方式。可以作为启动线程来处理客户机的替代方法的一种较老的技术是派生服务器赢博体育程序。在Linux中,当一个赢博体育程序分叉时,它会创建与自己完全相同的第二个副本。服务器可以通过为每个到达的客户机创建自己的新副本来同时处理多个客户机,这样每个客户机都可以获得自己的服务器赢博体育程序的私有副本以便与之交互。

下面是实现分叉策略所需的miniweb服务器赢博体育程序中的相关代码。这是main()中的循环,它等待新客户端连接并在客户端到达时为其提供服务:

当(1){struct sockaddr_in客户端;Int new_socket, c = sizeof(struct sockaddr_in);New_socket = accept(server_socket, (struct sockaddr *) &client, (socklen_t*)&c);If (new_socket != -1) {int pid;if((pid = fork()) == 0) {serveRequest(new_socket);退出(0);} else {printf(“已生成进程%d\n”,pid);关闭(new_socket);wait_pid(1空WNOHANG);}}

像往常一样,我们在这里使用accept()函数来等待新的客户机连接。Accept()在新客户端到达时返回套接字的文件描述符。

当客户端到达时,我们调用fork()函数来分叉服务器。在Linux中对一个正在运行的进程调用fork()会创建该进程的一个相同的副本。它产生两个进程:调用fork()的原始进程称为父进程,而由fork()产生的副本称为子进程。

父进程和子进程除了一个非常微小但重要的细节之外是相同的。在父进程中,调用fork()返回新创建的子进程的整数进程标识符。在子进程中,对fork()的调用返回值0。鉴于此,通常的编程约定是将对fork()的调用放在if语句中,该语句可以在父进程中使用一个分支,在子进程中使用另一个分支。这将允许子进程立即将自己与父进程区分开来。在分叉服务器的情况下,我们设置if语句,以便子分支继续处理客户端请求,而父分支返回侦听新的客户端连接。

分支服务器中的另一个约定是,子进程在为当前客户端提供服务后立即退出。要退出,子进程调用带有状态码的exit()。状态码为0表示完成成功。

当服务器分叉来处理客户端时,客户端获得父服务器中赢博体育内容的精确副本,包括赢博体育服务器的数据结构、打开的文件和打开的套接字连接。特别是,这意味着客户端可以使用服务器设置的客户端套接字。当客户端完成与该客户端的工作时,它将像往常一样关闭该套接字。

在服务器端,服务器也将拥有相同客户端套接字的副本。由于父进程不打算对该客户端做任何进一步的操作,因此父进程可以安全地关闭它的服务器套接字副本。这对子进程和客户端之间的通信没有不利影响。

当父进程启动子进程并且该子进程最终终止时,子进程将成为僵尸进程,并留下一些小数据结构,包括有关进程如何终止的信息。然后,父进程负责通过调用wait_pid()来获取不再活动的子进程。正如函数名所示,wait_pid()将等到子进程不再运行后才返回。在服务器的情况下,我们可能不希望阻止父进程来等待子进程完成运行,因为服务器将希望尽快返回侦听新连接。鉴于此,服务器使用wait_pid()参数的特殊配置。wait_pid()的第一个参数通常是我们正在等待的子进程的进程id号。取而代之的是,我们使用-1的值,这表明我们想要收获任何一个可用的子进程。wait_pid()的第三个参数是我们指定选项的地方:在本例中,我们使用WNOHANG选项,这表明如果当前没有准备好捕获的子进程,我们将不会等待一个可用的子进程。

尽管分叉服务器比线程服务器更容易实现,但由于许多原因,线程服务器仍然是首选。选择线程而不是子进程的第一个原因是,线程是一个比进程更轻量级的结构。创建多个线程与创建多个子进程相比需要更少的系统资源。选择线程的第二个更关键的原因是,赢博体育线程都运行在同一个进程中,并共享相同的内存空间。在赢博体育正在运行的线程都需要共享对公共数据结构的访问时,这一点尤其重要。这在子进程中是不可能容易做到的,因为每个子进程将继承该数据结构的自己的唯一副本:该客户端进程对数据结构所做的任何更改对父进程或其他子进程都不可见。