fork() 的目的是什么?

发布于 2024-07-24 08:26:27 字数 98 浏览 4 评论 0 原文

在 Linux 的许多程序和手册页中,我都看到过使用 fork() 的代码。 为什么我们需要使用fork()以及它的目的是什么?

In many programs and man pages of Linux, I have seen code using fork(). Why do we need to use fork() and what is its purpose?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

与往事干杯 2024-07-31 08:26:27

fork() 是在 Unix 中创建新进程的方法。 当您调用fork时,您正在创建自己的进程的副本,该副本拥有自己的地址空间。 这允许多个任务彼此独立运行,就好像它们各自拥有机器的完整内存一样。

以下是 fork 的一些示例用法:

  1. 您的 shell 使用fork 运行您从命令行调用的程序。
  2. apache 这样的 Web 服务器使用 fork 创建多个服务器进程,每个进程处理请求在自己的地址空间中。 如果一个进程死亡或内存泄漏,其他进程不会受到影响,因此它起到了一种容错机制的作用。
  3. Google Chrome 使用 fork 在单独的进程中处理每个页面。 这将防止一页上的客户端代码导致整个浏览器崩溃。
  4. fork 用于在某些并行程序中生成进程(例如使用 线程 不同,后者没有自己的地址空间,并且存在于进程内。
  5. 脚本语言间接使用fork来启动子进程。 例如,每次使用 subprocess.Popen 在 Python 中,您fork一个子进程并读取其输出。 这使得程序能够协同工作。

shell 中 fork 的典型用法可能如下所示:

int child_process_id = fork();
if (child_process_id) {
    // Fork returns a valid pid in the parent process.  Parent executes this.

    // wait for the child process to complete
    waitpid(child_process_id, ...);  // omitted extra args for brevity

    // child process finished!
} else {
    // Fork returns 0 in the child process.  Child executes this.

    // new argv array for the child process
    const char *argv[] = {"arg1", "arg2", "arg3", NULL};

    // now start executing some other program
    exec("/path/to/a/program", argv);
}

shell 使用 exec 生成一个子进程并等待其完成,然后继续执行自己的操作。 请注意,您不必以这种方式使用 fork。 您始终可以生成大量子进程,就像并行程序可能所做的那样,并且每个子进程都可以同时运行一个程序。 基本上,每当您在 Unix 系统中创建新进程时,您都会使用 fork()。 对于 Windows 等效项,请查看 CreateProcess

如果您想要更多示例和更长的解释,维基百科有一个不错的总结。 这里有一些幻灯片关于进程、线程和并发在现代操作系统中如何工作。

fork() is how you create new processes in Unix. When you call fork, you're creating a copy of your own process that has its own address space. This allows multiple tasks to run independently of one another as though they each had the full memory of the machine to themselves.

Here are some example usages of fork:

  1. Your shell uses fork to run the programs you invoke from the command line.
  2. Web servers like apache use fork to create multiple server processes, each of which handles requests in its own address space. If one dies or leaks memory, others are unaffected, so it functions as a mechanism for fault tolerance.
  3. Google Chrome uses fork to handle each page within a separate process. This will prevent client-side code on one page from bringing your whole browser down.
  4. fork is used to spawn processes in some parallel programs (like those written using MPI). Note this is different from using threads, which don't have their own address space and exist within a process.
  5. Scripting languages use fork indirectly to start child processes. For example, every time you use a command like subprocess.Popen in Python, you fork a child process and read its output. This enables programs to work together.

Typical usage of fork in a shell might look something like this:

int child_process_id = fork();
if (child_process_id) {
    // Fork returns a valid pid in the parent process.  Parent executes this.

    // wait for the child process to complete
    waitpid(child_process_id, ...);  // omitted extra args for brevity

    // child process finished!
} else {
    // Fork returns 0 in the child process.  Child executes this.

    // new argv array for the child process
    const char *argv[] = {"arg1", "arg2", "arg3", NULL};

    // now start executing some other program
    exec("/path/to/a/program", argv);
}

The shell spawns a child process using exec and waits for it to complete, then continues with its own execution. Note that you don't have to use fork this way. You can always spawn off lots of child processes, as a parallel program might do, and each might run a program concurrently. Basically, any time you're creating new processes in a Unix system, you're using fork(). For the Windows equivalent, take a look at CreateProcess.

If you want more examples and a longer explanation, Wikipedia has a decent summary. And here are some slides here on how processes, threads, and concurrency work in modern operating systems.

因为看清所以看轻 2024-07-31 08:26:27

fork() 是 Unix 创建新进程的方法。 当您调用 fork() 时,您的进程被克隆,并且两个不同的进程从那里继续执行。 其中一个(子级)将让 fork() 返回 0。另一个(父级)将让 fork() 返回子级的 PID(进程 ID)。

例如,如果您在 shell 中键入以下内容,shell 程序将调用 fork(),然后在子进程中执行您传递的命令(本例中为 telnetd),而父进程也会再次显示提示符作为指示后台进程 PID 的消息。

$ telnetd &

至于创建新进程的原因,这就是操作系统可以同时执行许多操作的方式。 这就是为什么您可以运行一个程序,并在其运行时切换到另一个窗口并执行其他操作。

fork() is how Unix create new processes. At the point you called fork(), your process is cloned, and two different processes continue the execution from there. One of them, the child, will have fork() return 0. The other, the parent, will have fork() return the PID (process ID) of the child.

For example, if you type the following in a shell, the shell program will call fork(), and then execute the command you passed (telnetd, in this case) in the child, while the parent will display the prompt again, as well as a message indicating the PID of the background process.

$ telnetd &

As for the reason you create new processes, that's how your operating system can do many things at the same time. It's why you can run a program and, while it is running, switch to another window and do something else.

思念满溢 2024-07-31 08:26:27

fork() 基本上用于为调用该函数的进程创建一个子进程。 每当您调用 fork() 时,它都会为子 id 返回零。

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

通过此您可以为父级和子级提供不同的操作,并利用多线程功能。

fork() is basically used to create a child process for the process in which you are calling this function. Whenever you call a fork(), it returns a zero for the child id.

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

by this you can provide different actions for the parent and the child and make use of multithreading feature.

归途 2024-07-31 08:26:27

fork() 用于创建子进程。 当调用 fork() 函数时,将生成一个新进程,并且 fork() 函数调用将为子进程和父进程返回不同的值。

如果返回值为 0,则表明您是子进程;如果返回值是数字(恰好是子进程 ID),则表明您是父进程。 (如果是负数,则分叉失败并且没有创建子进程)

http:// www.yolinux.com/TUTORIALS/ForkExecProcesses.html

fork() is used to create child process. When a fork() function is called, a new process will be spawned and the fork() function call will return a different value for the child and the parent.

If the return value is 0, you know you're the child process and if the return value is a number (which happens to be the child process id), you know you're the parent. (and if it's a negative number, the fork was failed and no child process was created)

http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

猫弦 2024-07-31 08:26:27

fork() 将创建一个与父进程相同的新子进程。 因此,您之后在代码中运行的所有内容都将由两个进程运行 - 如果您有一个服务器,并且您想要处理多个请求,则非常有用。

fork() will create a new child process identical to the parent. So everything you run in the code after that will be run by both processes — very useful if you have for instance a server, and you want to handle multiple requests.

因为看清所以看轻 2024-07-31 08:26:27

系统调用 fork() 用于创建进程。 它不带参数并返回进程 ID。 fork() 的目的是创建一个新进程,该新进程成为调用者的子进程。 创建新的子进程后,两个进程都将执行 fork() 系统调用后的下一条指令。 因此,我们必须区分父母和孩子。 这可以通过测试 fork() 的返回值来完成:

如果 fork() 返回负值,则子进程的创建不成功。
fork() 将零返回给新创建的子进程。
fork() 返回一个正值,即子进程的进程 ID 给父进程。 返回的进程ID是sys/types.h中定义的pid_t类型。 通常,进程 ID 是一个整数。 此外,进程可以使用函数 getpid() 来检索分配给该进程的进程 ID。
因此,在系统调用fork()之后,一个简单的测试就可以判断哪个进程是子进程。 请注意,Unix 将精确复制父地址空间并将其提供给子地址。 因此,父进程和子进程有独立的地址空间。

让我们通过一个例子来理解一下,以明确上述几点。 此示例不区分父进程和子进程。

#include  <stdio.h>
#include  <string.h>
#include  <sys/types.h>

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

假设上面的程序执行到调用 fork() 为止。

如果对 fork() 的调用成功执行,Unix 将制作两份相同的地址空间副本,一份用于父级,另一份用于子级。
两个进程都将在 fork() 调用之后的下一条语句处开始执行。 在这种情况下,两个进程都将在赋值时开始执行。

pid = .....;

两个进程在系统调用 fork() 后立即开始执行。 由于两个进程具有相同但独立的地址空间,因此在 fork() 调用之前初始化的那些变量在两个地址空间中具有相同的值。 由于每个进程都有自己的地址空间,因此任何修改都将独立于其他进程。 换句话说,如果父进程更改了其变量的值,则修改只会影响父进程地址空间中的变量。 由 fork() 调用创建的其他地址空间不会受到影响,即使它们具有相同的变量名称。

使用 write 而不是 printf 的原因是什么? 这是因为 printf() 是“缓冲的”,这意味着 printf() 会将进程的输出分组在一起。 在为父进程缓冲输出的同时,子进程也可能使用 printf 打印出一些信息,这些信息也将被缓冲。 因此,由于输出不会立即发送到屏幕,因此您可能无法获得预期结果的正确顺序。 更糟糕的是,两个进程的输出可能会以奇怪的方式混合。 为了克服这个问题,您可以考虑使用“无缓冲”写入。

如果运行该程序,您可能会在屏幕上看到以下内容:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

进程 ID 3456 可能是分配给父进程或子进程的进程。 由于这些进程同时运行,它们的输出线以一种相当不可预测的方式混合在一起。 此外,这些行的顺序由 CPU 调度程序确定。 因此,如果你再次运行这个程序,你可能会得到完全不同的结果。

System call fork() is used to create processes. It takes no arguments and returns a process ID. The purpose of fork() is to create a new process, which becomes the child process of the caller. After a new child process is created, both processes will execute the next instruction following the fork() system call. Therefore, we have to distinguish the parent from the child. This can be done by testing the returned value of fork():

If fork() returns a negative value, the creation of a child process was unsuccessful.
fork() returns a zero to the newly created child process.
fork() returns a positive value, the process ID of the child process, to the parent. The returned process ID is of type pid_t defined in sys/types.h. Normally, the process ID is an integer. Moreover, a process can use function getpid() to retrieve the process ID assigned to this process.
Therefore, after the system call to fork(), a simple test can tell which process is the child. Please note that Unix will make an exact copy of the parent's address space and give it to the child. Therefore, the parent and child processes have separate address spaces.

Let us understand it with an example to make the above points clear. This example does not distinguish parent and the child processes.

#include  <stdio.h>
#include  <string.h>
#include  <sys/types.h>

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

Suppose the above program executes up to the point of the call to fork().

If the call to fork() is executed successfully, Unix will make two identical copies of address spaces, one for the parent and the other for the child.
Both processes will start their execution at the next statement following the fork() call. In this case, both processes will start their execution at the assignment

pid = .....;

Both processes start their execution right after the system call fork(). Since both processes have identical but separate address spaces, those variables initialized before the fork() call have the same values in both address spaces. Since every process has its own address space, any modifications will be independent of the others. In other words, if the parent changes the value of its variable, the modification will only affect the variable in the parent process's address space. Other address spaces created by fork() calls will not be affected even though they have identical variable names.

What is the reason of using write rather than printf? It is because printf() is "buffered," meaning printf() will group the output of a process together. While buffering the output for the parent process, the child may also use printf to print out some information, which will also be buffered. As a result, since the output will not be send to screen immediately, you may not get the right order of the expected result. Worse, the output from the two processes may be mixed in strange ways. To overcome this problem, you may consider to use the "unbuffered" write.

If you run this program, you might see the following on the screen:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

Process ID 3456 may be the one assigned to the parent or the child. Due to the fact that these processes are run concurrently, their output lines are intermixed in a rather unpredictable way. Moreover, the order of these lines are determined by the CPU scheduler. Hence, if you run this program again, you may get a totally different result.

染火枫林 2024-07-31 08:26:27

如果您正在编写应用程序,您可能不需要在日常编程中使用 fork。

即使您确实希望程序启动另一个程序来执行某些任务,也有其他更简单的接口在幕后使用 fork,例如 C 和 perl 中的“system”。

例如,如果您希望应用程序启动另一个程序(例如 bc)来为您执行一些计算,则可以使用“system”来运行它。 系统执行“fork”来创建一个新进程,然后执行“exec”将该进程转换为 bc。 一旦 bc 完成,系统将控制权返回给您的程序。

您还可以异步运行其他程序,但我不记得如何操作。

如果您正在编写服务器、shell、病毒或操作系统,您更有可能想要使用 fork。

You probably don't need to use fork in day-to-day programming if you are writing applications.

Even if you do want your program to start another program to do some task, there are other simpler interfaces which use fork behind the scenes, such as "system" in C and perl.

For example, if you wanted your application to launch another program such as bc to do some calculation for you, you might use 'system' to run it. System does a 'fork' to create a new process, then an 'exec' to turn that process into bc. Once bc completes, system returns control to your program.

You can also run other programs asynchronously, but I can't remember how.

If you are writing servers, shells, viruses or operating systems, you are more likely to want to use fork.

救赎№ 2024-07-31 08:26:27

多处理是计算的核心。 例如,您的 IE 或 Firefox 可以创建一个进程,在您仍在浏览互联网时为您下载文件。 或者,当您在文字处理程序中打印文档时,您仍然可以查看不同的页面并对其进行一些编辑。

Multiprocessing is central to computing. For example, your IE or Firefox can create a process to download a file for you while you are still browsing the internet. Or, while you are printing out a document in a word processor, you can still look at different pages and still do some editing with it.

慢慢从新开始 2024-07-31 08:26:27

Fork 创建新进程。 如果没有 fork,你将拥有一个只能运行 init 的 unix 系统。

Fork creates new processes. Without fork you would have a unix system that could only run init.

深海里的那抹蓝 2024-07-31 08:26:27

Fork() 用于创建新进程,正如每个人都写的那样。

这是我以二叉树形式创建进程的代码......它将要求扫描您要在二叉树

#include<unistd.h> 
#include<fcntl.h> 
#include<stdlib.h>   
int main() 
{
int t1,t2,p,i,n,ab;
p=getpid();                
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);                
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)    
{        
    t1=fork();

    if(t1!=0)
        t2=fork();        
    if(t1!=0 && t2!=0)        
        break;            
    printf("child pid %d   parent pid %d\n",getpid(),getppid());fflush(stdout);
}   
    waitpid(t1,&ab,0);
    waitpid(t2,&ab,0);
return 0;
}

输出中创建进程的级别数

  enter the number of levels
  3
  root 20665
  child pid 20670   parent pid 20665
  child pid 20669   parent pid 20665
  child pid 20672   parent pid 20670
  child pid 20671   parent pid 20670
  child pid 20674   parent pid 20669
  child pid 20673   parent pid 20669

Fork() is used to create new processes as every body has written.

Here is my code that creates processes in the form of binary tree.......It will ask to scan the number of levels upto which you want to create processes in binary tree

#include<unistd.h> 
#include<fcntl.h> 
#include<stdlib.h>   
int main() 
{
int t1,t2,p,i,n,ab;
p=getpid();                
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);                
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)    
{        
    t1=fork();

    if(t1!=0)
        t2=fork();        
    if(t1!=0 && t2!=0)        
        break;            
    printf("child pid %d   parent pid %d\n",getpid(),getppid());fflush(stdout);
}   
    waitpid(t1,&ab,0);
    waitpid(t2,&ab,0);
return 0;
}

OUTPUT

  enter the number of levels
  3
  root 20665
  child pid 20670   parent pid 20665
  child pid 20669   parent pid 20665
  child pid 20672   parent pid 20670
  child pid 20671   parent pid 20670
  child pid 20674   parent pid 20669
  child pid 20673   parent pid 20669
多孤肩上扛 2024-07-31 08:26:27

首先需要了解什么是fork()系统调用。 让我解释一下

  1. fork()系统调用创建父进程的精确副本,它复制父进程的堆栈、堆、初始化数据、未初始化数据,并以只读模式与父进程共享代码。

  2. Fork 系统调用以写时复制为基础复制内存,意味着子进程在需要复制时在虚拟内存页面中创建页面。

    Fork 系统

现在fork()的用途:

  1. fork()可以用在有分工的地方,比如服务器必须处理多个客户端,所以父级必须定期接受连接,所以服务器为每个客户端进行fork执行读写。

First one needs to understand what is fork () system call. Let me explain

  1. fork() system call creates the exact duplicate of parent process, It makes the duplicate of parent stack, heap, initialized data, uninitialized data and share the code in read-only mode with parent process.

  2. Fork system call copies the memory on the copy-on-write basis, means child makes in virtual memory page when there is requirement of copying.

Now Purpose of fork():

  1. Fork() can be used at the place where there is division of work like a server has to handle multiple clients, So parent has to accept the connection on regular basis, So server does fork for each client to perform read-write.
孤星 2024-07-31 08:26:27

fork() 与仅使用 exec() 函数来启动新进程的基本原理在 unix 堆栈交换上类似问题的答案

本质上,由于 fork 复制了当前进程,因此进程的所有各种可能选项都是默认建立的,因此程序员无需提供它们。

相比之下,在 Windows 操作系统中,程序员必须使用 CreateProcess 函数,该函数要复杂得多,并且需要填充多种结构来定义新进程的参数。

因此,总而言之,分叉(相对于执行)的原因是创建新进程的简单性。

The rationale behind fork() versus just having an exec() function to initiate a new process is explained in an answer to a similar question on the unix stack exchange.

Essentially, since fork copies the current process, all of the various possible options for a process are established by default, so the programmer does not have supply them.

In the Windows operating system, by contrast, programmers have to use the CreateProcess function which is MUCH more complicated and requires populating a multifarious structure to define the parameters of the new process.

So, to sum up, the reason for forking (versus exec'ing) is simplicity in creating new processes.

蓝色星空 2024-07-31 08:26:27

fork() 用于生成子进程。 通常,它用于与线程类似的情况,但也存在差异。 与线程不同,fork() 创建整个独立的进程,这意味着在调用 fork() 时,子进程和父进程是彼此的直接副本,它们是完全独立的,都不能访问对方的内存空间(不会遇到访问另一个程序内存的正常麻烦)。

fork() 仍然被一些服务器应用程序使用,大多数是在 *NIX 机器上以 root 身份运行的应用程序,在处理用户请求之前会删除权限。 还有一些其他用例,但现在大多数人已经转向多线程。

fork() is used to spawn a child process. Typically it's used in similar sorts of situations as threading, but there are differences. Unlike threads, fork() creates whole seperate processes, which means that the child and the parent while they are direct copies of each other at the point that fork() is called, they are completely seperate, neither can access the other's memory space (without going to the normal troubles you go to access another program's memory).

fork() is still used by some server applications, mostly ones that run as root on a *NIX machine that drop permissions before processing user requests. There are some other usecases still, but mostly people have moved to multithreading now.

苦笑流年记忆 2024-07-31 08:26:27

Fork() 的创建是为了创建另一个进程并与父进程共享内存状态的副本。 它之所以如此工作,是因为在以前缺乏此功能的时间切片大型机系统中获得良好的线程功能是可能的最小改变。 此外,程序只需很少的修改即可成为多进程,只需将 fork() 添加到适当的位置即可,这是相当优雅的。 基本上,fork() 是阻力最小的路径。

本来它实际上必须复制整个父进程的内存空间。 随着虚拟内存的出现,它已经被修改并变得更加高效,写入时复制机制避免了实际复制任何内存的需要。

然而,现代系统现在允许创建实际线程,这些线程只是共享父进程的实际堆。 对于现代多线程编程范例和更高级的语言,fork() 是否提供任何真正的好处是值得怀疑的,因为 fork() 实际上阻止进程直接通过内存进行通信,并迫使它们使用较慢的消息传递机制。

Fork() was created as a way to create another process with shared a copy of memory state to the parent. It works the way it does because it was the most minimal change possible to get good threading capabilities in time-slicing mainframe systems that previously lacked this capability. Additionally, programs needed remarkably little modification to become multi-process, fork() could simply be added in the appropriate locations, which is rather elegant. Basically, fork() was the path of least resistance.

Originally it actually had to copy the entire parent process' memory space. With the advent of virtual memory, it has been hacked and changed to be more efficient, with copy-on-write mechanisms avoiding the need to actual copy any memory.

However, modern systems now allow the creation of actual threads, which simply share the parent process' actual heap. With modern multi-threading programming paradigms and more advanced languages, it's questionable whether fork() provides any real benefit, since fork() actually prevents processes from communicating through memory directly, and forces them to use slower message passing mechanisms.

夏雨凉 2024-07-31 08:26:27

Fork() 系统调用用于创建子进程。 它是父进程的精确副本。 Fork 从父级复制堆栈部分、堆部分、数据部分、环境变量、命令行参数。

参考:http://man7.org/linux/man-pages/ man2/fork.2.html

Fork() system call use to create a child process. It is exact duplicate of parent process. Fork copies stack section, heap section, data section, environment variable, command line arguments from parent.

refer: http://man7.org/linux/man-pages/man2/fork.2.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文