Confirmed users
85
edits
No edit summary |
No edit summary |
||
| Line 6: | Line 6: | ||
= Architecture = | = Architecture = | ||
[[File:Ipc process architecture.png|300x300px|framed|right|Process architecture]] | [[File:Ipc process architecture.png|300x300px|framed|right|Process architecture]] | ||
In FirefoxOS we have a Multi-process Architecture, where the apps on the phone are running in a different process, which has the least amount of | In FirefoxOS we have a Multi-process Architecture, where the apps on the phone are running in a different process, which has the least amount of privileges. On a running system we have a single parent process called ''b2g''. The ''b2g'' process has a child process called ''nuwa'', this process will be used to fork the app processes. Whenever a new app is being started on the phone, ''b2g'' tells ''nuwa'' that we need a new process. All child processes have the least amount of privileges possible. For every action they want to perform (that needs more privileges), they need to go through the parent process (b2g). This is where the Inter-process Communication (IPC) comes into place. Each child has a connection, using an IPC channel, to the parent which will be used for their communication. The process layout is illustrated on the right. | ||
= Setup = | = Setup = | ||
Now that you have a rough overview of how everything is | Now that you have a rough overview of how everything is laid out, we will go into more detail on how this communication actually works. | ||
We use Unix sockets, created with the '''socketpair''' system call, to send messages across the process boundary. For writing and reading, the system calls '''sendmsg''' and '''recvmsg''' are being used. Each process has its own dedicated thread that handles the socket operations, it is called ''IOLoop''. Each ''IOLoop'' thread has its own outgoing message queue, which is used by the main thread to send a new message across the channel. | We use Unix sockets, created with the '''socketpair''' system call, to send messages across the process boundary. For writing and reading, the system calls '''sendmsg''' and '''recvmsg''' are being used. Each process has its own dedicated thread that handles the socket operations, it is called ''IOLoop''. Each ''IOLoop'' thread has its own outgoing message queue, which is used by the main thread to send a new message across the channel. | ||
| Line 29: | Line 29: | ||
</pre> | </pre> | ||
'''''ioThread''''' is an instance of '''[https://github.com/mozilla/gecko-dev/blob/82ff7027aac0f7578d5c26567d8ac8e4b5d2b647/ipc/glue/BrowserProcessSubThread.h BrowserProcessSubThread]''' which inherits from '''[https://github.com/mozilla/gecko-dev/blob/e72ffaed408cab53a4cf4ca152089c9c38d2921c/ipc/chromium/src/base/thread.h base::Thread]''', itself a | '''''ioThread''''' is an instance of '''[https://github.com/mozilla/gecko-dev/blob/82ff7027aac0f7578d5c26567d8ac8e4b5d2b647/ipc/glue/BrowserProcessSubThread.h BrowserProcessSubThread]''' which inherits from '''[https://github.com/mozilla/gecko-dev/blob/e72ffaed408cab53a4cf4ca152089c9c38d2921c/ipc/chromium/src/base/thread.h base::Thread]''', itself a subclass of '''[https://github.com/mozilla/gecko-dev/blob/e2ff646a3cd6d91020d037e633c9776026871b71/ipc/chromium/src/base/platform_thread.h PlatformThread::Delegate]'''. | ||
'''StartWithOptions''' is actually defined in '''base::Thread''', the call will lead to a couple of more calls which will eventually end up at '''pthread_create'''. The function started in the new thread is '''[https://github.com/mozilla/gecko-dev/blob/e2ff646a3cd6d91020d037e633c9776026871b71/ipc/chromium/src/base/platform_thread_posix.cc#L36 ThreadFunc]'''. '''''ioThread''''' object is passed along the calls and '''''ioThread''->[https://github.com/mozilla/gecko-dev/blob/e2ff646a3cd6d91020d037e633c9776026871b71/ipc/chromium/src/base/thread.cc#L139 ThreadMain()]''' is called in the new thread. | '''StartWithOptions''' is actually defined in '''base::Thread''', the call will lead to a couple of more calls which will eventually end up at '''pthread_create'''. The function started in the new thread is '''[https://github.com/mozilla/gecko-dev/blob/e2ff646a3cd6d91020d037e633c9776026871b71/ipc/chromium/src/base/platform_thread_posix.cc#L36 ThreadFunc]'''. '''''ioThread''''' object is passed along the calls and '''''ioThread''->[https://github.com/mozilla/gecko-dev/blob/e2ff646a3cd6d91020d037e633c9776026871b71/ipc/chromium/src/base/thread.cc#L139 ThreadMain()]''' is called in the new thread. | ||
| Line 86: | Line 86: | ||
It pretty much comes down to the creation of an '''[https://github.com/mozilla/gecko-dev/blob/eacb45e46d4b67a0d309e44af5de1ebede88d27b/ipc/chromium/src/chrome/common/ipc_channel.h#L15 IPC::Channel]''' instance (the process of creating the instance will be covered later on). This class has two important attributes: | It pretty much comes down to the creation of an '''[https://github.com/mozilla/gecko-dev/blob/eacb45e46d4b67a0d309e44af5de1ebede88d27b/ipc/chromium/src/chrome/common/ipc_channel.h#L15 IPC::Channel]''' instance (the process of creating the instance will be covered later on). This class has two important attributes: | ||
* '''channel_impl_''' which is the actual implementation of the channel (platform specific) | * '''channel_impl_''' which is the actual implementation of the channel (platform specific) | ||
* '''listener_''' this is used to pass | * '''listener_''' this is used to pass incoming messages to | ||
the posix class for the '''channel_impl_''' object can be found here ('''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.h#L24 Channel::ChannelImpl]'''). | the posix class for the '''channel_impl_''' object can be found here ('''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.h#L24 Channel::ChannelImpl]'''). | ||
| Line 96: | Line 96: | ||
* '''output_queue_''' a queue where all outgoing messages are pushed to | * '''output_queue_''' a queue where all outgoing messages are pushed to | ||
'''Channel::ChannelImpl''' has two overloaded | '''Channel::ChannelImpl''' has two overloaded constructor which can be used to create an object. One of them takes a file descriptor as the first argument which will be stored in '''pipe_'''. The more interesting constructor is the one which takes a '''channel_id''' (can also be empty). Both of them also take a '''Mode''' and a '''Listener*''' pointer as second and third argument. '''Mode''' just specifies if we are the server or the client. | ||
When the constructor with the '''channel_id''' is called, '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L303 CreatePipe]''' will be called from there. We have to distinguish two different cases from here: | When the constructor with the '''channel_id''' is called, '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L303 CreatePipe]''' will be called from there. We have to distinguish two different cases from here: | ||
| Line 110: | Line 110: | ||
</blockquote> | </blockquote> | ||
In the end, '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L372 EnqueueHelloMessage]''' is called which will push the | In the end, '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L372 EnqueueHelloMessage]''' is called which will push the initial hello message onto the output queue. | ||
After the object creation is completed, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L395 Connect]''' method can be called, this method will tell ''libevent'' to notify us whenever something has been written to '''pipe_''' and is ready to be received. | After the object creation is completed, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L395 Connect]''' method can be called, this method will tell ''libevent'' to notify us whenever something has been written to '''pipe_''' and is ready to be received. | ||
| Line 126: | Line 126: | ||
== Nuwa == | == Nuwa == | ||
=== creating the process === | === creating the process === | ||
Throughout the initialization phase of the ''b2g'' process, an instance of the singleton class '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L36 PreallocatedProcessManagerImpl]''' will be created. This instance is mainly accessed through a couple of static functions defined in the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.h#L39 PreallocatedProcessManager]''' class. The purpose of this manager is to keep track of pre-allocated processes, this will be explained in more detail in the [[#Preallocated]] | Throughout the initialization phase of the ''b2g'' process, an instance of the singleton class '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L36 PreallocatedProcessManagerImpl]''' will be created. This instance is mainly accessed through a couple of static functions defined in the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.h#L39 PreallocatedProcessManager]''' class. The purpose of this manager is to keep track of pre-allocated processes, this will be explained in more detail in the [[#Preallocated]] section. | ||
The implementation class has two important attributes: | The implementation class has two important attributes: | ||
| Line 132: | Line 132: | ||
* '''mPreallocatedAppProcess''' which will be the ''nuwa'' process | * '''mPreallocatedAppProcess''' which will be the ''nuwa'' process | ||
This initialization happens inside the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/ContentParent.cpp#L602 ContentParent::StartUp]''' function, when | This initialization happens inside the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/ContentParent.cpp#L602 ContentParent::StartUp]''' function, when executing the following code: | ||
<pre> | <pre> | ||
... | ... | ||
| Line 140: | Line 140: | ||
</pre> | </pre> | ||
This call will lead to the creation of the one and only instance of '''PreallocatedProcessManagerImpl''' is created (inside the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L102 PreallocatedProcessManagerImpl::Singleton]''' function). Right after the constructor call, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L125 Init]''' function is invoked. Following the call flow from there, we will end up in '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L174 Enable]'''. '''Enable''' will then schedule the ''nuwa'' fork, with a 1 second delay ([https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L23 DEFAULT_ALLOCATE_DELAY]), by calling '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L237 ScheduleDelayedNuwaFork]'''. This gives the ''b2g'' process enough time to finish its | This call will lead to the creation of the one and only instance of '''PreallocatedProcessManagerImpl''' is created (inside the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L102 PreallocatedProcessManagerImpl::Singleton]''' function). Right after the constructor call, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L125 Init]''' function is invoked. Following the call flow from there, we will end up in '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L174 Enable]'''. '''Enable''' will then schedule the ''nuwa'' fork, with a 1 second delay ([https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L23 DEFAULT_ALLOCATE_DELAY]), by calling '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L237 ScheduleDelayedNuwaFork]'''. This gives the ''b2g'' process enough time to finish its initialization. | ||
As soon as the delay time has passed, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L255 DelayedNuwaFork]''' function is called inside the main thread. | As soon as the delay time has passed, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/PreallocatedProcessManager.cpp#L255 DelayedNuwaFork]''' function is called inside the main thread. | ||
| Line 193: | Line 193: | ||
After a instance of '''ProcessLink''' is created, we call '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/glue/MessageLink.cpp#L83 Open]''' on it. This sets the member variable '''mTransport''' (which represents the IPC::Channel). So '''mTransport''' is the '''IPC::Channel''' pointer we retrieved from '''mSubprocess''' | After a instance of '''ProcessLink''' is created, we call '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/glue/MessageLink.cpp#L83 Open]''' on it. This sets the member variable '''mTransport''' (which represents the IPC::Channel). So '''mTransport''' is the '''IPC::Channel''' pointer we retrieved from '''mSubprocess''' | ||
Since '''Connect''' has already been called when the '''IPC::Channel''' was created, we will schedule '''IOLoop''' to run '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/glue/MessageLink.cpp#L309 OnTakeConnectedChannel]'''. The channel state inside the '''MessageChannel''' object will be set to '''ChannelConnected''' and we also call '''[https://github.com/mozilla/gecko-dev/blob/eacb45e46d4b67a0d309e44af5de1ebede88d27b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L962 set_listener]''' on the '''IPC::Channel''' instance to let it know, that '''ProcessLink''' will handle incoming messages, that means '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/glue/MessageLink.cpp#L270 OnMessageReceived]''' inside '''ProcessLink''' is called. | Since '''Connect''' has already been called when the '''IPC::Channel''' was created, we will schedule '''IOLoop''' to run '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/glue/MessageLink.cpp#L309 OnTakeConnectedChannel]'''. The channel state inside the '''MessageChannel''' object will be set to '''ChannelConnected''' and we also call '''[https://github.com/mozilla/gecko-dev/blob/eacb45e46d4b67a0d309e44af5de1ebede88d27b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L962 set_listener]''' on the '''IPC::Channel''' instance to let it know, that '''ProcessLink''' will handle incoming messages, that means '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/glue/MessageLink.cpp#L270 OnMessageReceived]''' inside '''ProcessLink''' is called. Those received messages will be passed to the '''MessageChannel''' and from there to the '''OnMessageReceived''' funciton inside the '''PContentParent''' class. | ||
[[File:Ipc parent channel connect.png| | [[File:Ipc parent channel connect.png|900px|frameless|center|Call flow for set different listener]] | ||
Now everything has been setup on the parent side. Let's get to the ''nuwa'' side. | |||
After the '''fork''', ''nuwa'' inherited all the open file descriptors from the parent ''b2g'', one of them is its end of the pipe. Every child expects its end of the pipe to be file descriptor 3 ('''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/ipc_channel_posix.cc#L126 kClientChannelFd]'''). In order to guarantee that file descriptor 3 will be the child's end of the pipe, we call '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/base/file_descriptor_shuffle.h#L72 ShuffleFileDescriptors]''', this function will '''dup''' the child end of the pipe to 3. It also makes sure that in case 3 is a file descriptor needed by the child, that it will be remapped to the next available. | |||
After the file descriptors have been remapped, we call '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/base/process_util_posix.cc#L107 CloseSuperfluousFds]''' to close all the ones that ''nuwa'' doesn't need. When everything is done, the child will call '''execve''' to re-execute itself. | |||
From here please have a look at the [[#Child]] section, the actual channel connection will happen inside the '''ThreadMain''' function inside the ''IOLoop'' thread. '''ThreadMain''' will call '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/child_thread.cc#L91 Init]''' defined in '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/ipc/chromium/src/chrome/common/child_thread.h#L15 ChildThread]'''. Inside this function, we will create a new '''IPC::Channel''' object, and you can refer to the [[#Channel]] section from here. You will have to consider the part Mode != MODE_SERVER, therefore '''ChannelNameToClientFD''' will be called, which returns our magic file descriptor 3 (remember the child end of the pipe is mapped to this fd). | |||
So now, we have a connection to the channel, what is left is to set the correct listener for the incoming messages. To do so, the '''[https://github.com/mozilla/gecko-dev/blob/6f80c55849c36f46f48941f735c8228b7e2f36d0/dom/ipc/ContentProcess.cpp#L23 Init]''' funciton inside '''ContentProcess''' is called. From there it will call '''Open''' in the '''PContentChild''' class, this will lead to the same call flow as for the parent in the above diagram. The only exception we have is that these calls originate from '''ContentChild''' which inherits from '''PContentChild'''. | |||
== Preallocated == | == Preallocated == | ||