如果我有一个进程,并且克隆它,PID 是否相同?
只是一个简单的问题,如果我克隆一个进程,克隆进程的 PID 是相同的,是吗? fork() 创建一个子进程,其中 PID 不同,但其他一切都相同。 Vfork() 创建一个具有相同 PID 的子进程。 Exec 的作用是将当前正在执行的进程更改为其他进程。
我的所有这些陈述都正确吗?
Just a quick question, if I clone a process, the PID of the cloned process is the same, yes ? fork() creates a child process where the PID differs, but everything else is the same. Vfork() creates a child process with the same PID. Exec works to change a process currently in execution to something else.
Am I correct in all of these statements ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
不完全是。如果您通过 fork/exec 或 vfork/exec 克隆进程,您将获得一个新的进程 ID。
fork()
将为您提供具有新进程 ID 的新进程,而exec()
会用新进程替换该进程,但保留进程 ID。来自此处:
Not quite. If you clone a process via fork/exec, or vfork/exec, you will get a new process id.
fork()
will give you the new process with a new process id, andexec()
replaces that process with a new process, but maintaining the process id.From here:
尽管
clone()
在一种情况下可以,但fork()
和vfork()
都不会保持相同的 PID (*a)。它们都是通过不同的方式来达到大致相同的目的,即创建一个不同的孩子。clone()
类似于fork()
但两个进程共享许多东西,这通常用于启用线程。vfork()
是clone
的变体,其中父进程会暂停,直到子进程退出或执行另一个程序。在这些情况下它更有效,因为它不涉及复制页表等。基本上,只要子进程需要加载另一个程序,所有内容都在两个进程之间共享。将最后一个选项与普通的写时复制进行对比,其中内存本身是共享的(直到其中一个进程写入它),但引用该内存的页表被复制。换句话说,
vfork()
甚至比写时复制更高效,至少对于 fork-followed-by-immediate-exec 用例来说是如此。但是,在大多数情况下,子进程与父进程具有不同的进程 ID。
*a 当您使用
CLONE_THREAD
进行clone()
时,事情会变得棘手。在此阶段,进程仍然具有不同的标识符,但 PID 的构成开始变得模糊。在最深层次上,Linux 调度程序不关心进程,它调度线程。线程具有线程ID(TID)和线程组ID(TGID)。 TGID 是从
getpid()
获得的。当线程在没有
CLONE_THREAD
的情况下克隆时,它会被赋予一个新的TID,并且它的TGID也会设置为该值(即,一个全新的PID)。使用
CLONE_THREAD
,它被赋予一个新的TID,但TGID(因此报告的进程ID)与父进程保持相同,因此它们实际上具有相同的PID。然而,它们可以通过从gettid()
获取 TID 来区分自己。关于父进程 ID 和信号传递(既到组内的线程,又到父进程的
SIGCHLD
),存在相当多的诡计,所有这些都可以从 < a href="http://linux.die.net/man/2/clone" rel="nofollow">clone()
手册页。Neither
fork()
norvfork()
keep the same PID althoughclone()
can in one scenario (*a). They are all different ways to achieve roughly the same end, the creation of a distinct child.clone()
is likefork()
but there are many things shared by the two processes and this is often used to enable threading.vfork()
is a variant ofclone
in which the parent is halted until the child process exits or executes another program. It's more efficient in those cases since it doesn't involve copying page tables and such. Basically, everything is shared between the two processes for as long as it takes the child to load another program.Contrast that last option with the normal copy-on-write where memory itself is shared (until one of the processes writes to it) but the page tables that reference that memory are copied. In other words,
vfork()
is even more efficient than copy-on-write, at least for the fork-followed-by-immediate-exec use case.But, in most cases, the child has a different process ID to the parent.
*a Things become tricky when you
clone()
withCLONE_THREAD
. At that stage, the processes still have different identifiers but what constitutes the PID begins to blur. At the deepest level, the Linux scheduler doesn't care about processes, it schedules threads.A thread has a thread ID (TID) and a thread group ID (TGID). The TGID is what you get from
getpid()
.When a thread is cloned without
CLONE_THREAD
, it's given a new TID and it also has its TGID set to that value (i.e., a brand new PID).With
CLONE_THREAD
, it's given a new TID but the TGID (hence the reported process ID) remains the same as the parent so they really have the same PID. However, they can distinguish themselves by getting the TID fromgettid()
.There's quite a bit of trickery going on there with regard to parent process IDs and delivery of signals (both to the threads within a group and the
SIGCHLD
to the parent), all which can be examined from theclone()
man page.它值得一些解释。这就像下雨一样简单。
考虑一下这一点。一个程序必须同时做一些事情。假设,您的程序每秒打印“hello world!”,直到有人输入“hello,Mike”,然后,每秒打印该字符串,等待约翰将来更改它。
你如何以标准方式写这个?在您的程序中,基本上打印“hello”,您必须创建另一个等待用户输入的分支。
您创建两个进程,一个输出这些字符串,另一个等待用户输入。而且,在 UNIX 中创建新进程的唯一方法是调用系统调用 fork(),如下所示:
这种方案带来了许多问题。用户输入“Mike”,但您没有简单的方法将该字符串传递给父进程以便它能够打印该字符串,因为每个+进程都有自己的内存视图,不与子进程共享。
当进程由 fork() 创建时,每个进程都会收到当时内存现有的副本,并且如果该内存确实发生变化稍后,这些内存段的相同映射将立即被更改(这称为写入时复制机制)。
子级和父级之间共享的另一个内容是,例如,打开的文件描述符、共享内存的描述符、输入/输出内容等,这些在 fork() 之后也不会保留。
所以。 fork() 调用必须得到缓解,包括共享内存/信号等。但是如何呢?这就是clone() 背后的想法。该调用需要一个标志,指示您将与孩子分享什么内容。例如,内存、信号处理程序等。如果您使用 flag=0 调用它,则这将与 fork() 相同,直到它们采用的参数为止。当创建 POSIX pthread 时,该标志将反映您在 pthread_attr 中指示的属性。
从内核的角度来看,这样创建的进程之间没有区别,也没有特殊的语义来区分“进程”。内核甚至不知道“线程”是什么,它创建了一个新进程,但它只是将其组合为属于父进程组的进程组调用它,并注意该过程可能会做什么。因此,不同的进程(共享相同的 pid)组合在一个进程组中,每个进程组分配有不同的“TID”(从父进程的 PID 开始)。
需要解释一下,clone() 正是这样做的。您可以传递您需要的任何(事实上,旧的 vfork() 调用就可以了)。你要分享记忆吗?汉勒斯?您可以调整所有内容,只要确保不会与围绕此调用立即编写的 pthreads 库发生冲突即可。
重要的是,内核版本非常离谱,它只期望传递 4 个参数中的 2 个,即用户堆栈和选项。
It deserves some explanation. And it's simple as rain.
Consider this. A program has to do some things at the same time. Say, your program is printing "hello world!", each second, until somebody enters "hello, Mike", then, each second, it prints that string, waiting for John to change that in the future.
How do you write this the standard way? In your program, that basically prints "hello," you must create another branch that is waiting for user input.
You create two processes, one outputting those strings, and another one, waiting the user input. And, the only way to create a new process in UNIX was calling the system call fork(), like this:
This scheme posed numerous problems. The user enters "Mike" but you have no simple way to pass that string to the parent process so that it'd be able to print that, because +each+ process has its own view of memory that isn't shared with the child.
When the processes are created by fork(), each one receives a copy of the memory existing at that moment, and if that memory really changes later, the mapping that was identical for those memory segments will be chaged at once (it's called a copy-on-write mechanism).
Another thingies to share between the child and the parent are, for example, opened file descriptors, descriptors of the shared memory, input/outpue stuff, etc., that also wouldn't survive after fork().
So. The very fork() call had to be alleviated, to include shared memory/signals etc. But how? This was the idea behind clone(). That call takes a flag indicating what exatly would you share with the child. For example, the memory, the signal handlers, etc. And if you call this with flag=0, this will be identical to fork(), up to the args they take. And when POSIX pthreads are created, that flag will reflect the attributes you have indicated in pthread_attr.
From the kernel point of view, there's no difference between the processes created such way, and no special semantics to differentiate the "processess". The kernel does not even know, what that "thread" is, it creates a new process, but it simply combines it as belogning to that process group that had the parent who called it, taking care what that process may do. So, you have different procesess (that share the same pid) combined in a process group each assigned with a different "TID" (that starts from PID of the parent).
Care to explain that clone() does exactly that. You may pass this whaterver you need (as the matter of fact, the old vfork() call will do). Are you going to share memory? Hanlers? You may tune everything, just be sure you don't clash with the pthreads library written right away around this very call.
An important thing, the kernel vesion is quite outrageous, it expects just 2 out of 4 parameters to be passed, the user stack, and options.
由于 PID 是进程的唯一标识符,因此无法让两个不同的进程具有相同的 PID。
Since PID is an unique identifier for a process, there's no way to have two distinct process with the same PID.
线程(具有相同的可见“pid”)是通过
clone()
调用实现的。当提供标志 CLONE_THREAD 时,新进程(“线程”)与其创建者进程共享线程组标识符(TGID)。getpid
实际上返回的是 TGID。有关更多详细信息,请参阅克隆手册页。
总之,内核看到的真实 PID 总是不同的。线程的可见PID是相同的。
Threads (which have the same visible 'pid') are implemented with the
clone()
call. When the flag CLONE_THREAD is supplied then the new process (a 'thread') share the Thread Group Identifier (TGID) with its creator process.getpid
actually returns the TGID.See the clone manpage for more details.
In summary the real PID, as seen by the kernel is always different. The visible PID is the same for threads.