Distributed Systems — Prof. Omicini

Code Mobility

DS-C6Andrea Omicini — DISI, Univ. Bologna A.Y. 2025/2026Based on Fuggetta et al., 1998

In this lesson

1. From Data to Code: Why Mobility Matters

In the traditional client-server model of distributed systems, what travels across the network is data. A client sends a request; the server processes it and returns a result. The code that performs the computation stays put. For many applications, this paradigm is sufficient and elegant.

But sometimes passing data is not enough. Consider a search engine crawling the web: shipping every page to a central indexer is hopelessly inefficient compared to sending a crawler program to each host and letting it extract and return only the relevant information. Consider a mobile agent that visits multiple marketplaces, negotiates on your behalf, and returns with the best deal — the agent carries both its behaviour and its evolving state as it roams.

Key idea

Code mobility is the capability to dynamically change the bindings between the components of a distributed system — relocating computations, data, or both — at runtime. When the cost of moving data exceeds the cost of moving code, or when you want to keep data and the code that processes it together, code mobility becomes the right abstraction.

Two fundamental motivations drive code mobility:

Editor's note

The foundational reference for code mobility is Fuggetta, Picco, and Vigna, "Understanding Code Mobility," IEEE TSE 24(5), 1998 — the paper that first gave the field a unified conceptual framework. Much of the terminology in this lesson traces back to that work.

2. Reasons for Migrating Code

Process migration — moving a running computation along with its context from one machine to another — is the traditional form of code mobility. The literature on process migration (see Milojicic et al., 2000) identifies several motivations, all of which remain relevant today:

ReasonWhat it means
Load balancingRedistribute computation across nodes to avoid hotspots and utilise idle resources.
Minimising communicationMove computation close to the data it processes, reducing the volume and latency of network traffic.
Optimising perceived performancePlace code where it can respond faster to users (e.g., edge computing, CDN edge functions).
Improving scalabilityDynamically spawn or relocate components as the system grows, without central bottlenecks.
Dynamic configurabilityInject new behaviour into a running system without redeploying the whole application.
Fault toleranceMigrate processes away from failing or soon-to-be-maintained machines.
The examiner may ask

"Give three concrete scenarios where code mobility is preferable to data mobility, and justify each." Be ready to contrast code mobility with the alternatives — remote procedure calls, message passing, or shipping data — for each scenario.

3. The Three Segments of a Process

To understand what it means to "move code", we need a precise model of what a running process actually consists of. Fuggetta et al. decompose a process into three conceptual segments:

SegmentContainsExamples
Code segmentThe set of executable instructions composing the process — the program text.Compiled bytecode, JavaScript source, a WASM binary.
Execution segmentThe private data, the stack, and the program counter — the current execution state.Local variables, call stack frames, the next instruction pointer.
Resource segmentReferences to external resources the process depends on to run.File handles, socket connections, printers, database connections, other processes.
Key idea

The kind of code mobility you achieve depends on which of these three segments are transferred from the source machine to the target. The code segment alone gives weak mobility; the code plus execution segment gives strong mobility; the resource segment brings the hardest challenges.

This decomposition is the conceptual backbone of the entire code mobility taxonomy. Every model we will study is defined by which segments travel, who initiates the transfer, and how the migrated code relates to the target environment.

graph TD
    P[Process] --> CS[Code Segment
Executable instructions] P --> ES[Execution Segment
Private data, stack, PC] P --> RS[Resource Segment
External references] CS --> |Transfer| WM[Weak Mobility] CS --> |Transfer| SM[Strong Mobility] ES --> |Transfer| SM RS --> |Bindings matter| MIG[Determines migration
actions at target]

4. Weak Mobility: Transferring the Code Segment

Weak mobility is the simplest form of code migration: only the code segment is transferred, possibly accompanied by some initialisation parameters. The execution state is not carried along — the code starts afresh at the destination, as if invoked for the first time.

The main idea is straightforward: the code can be executed ex novo every time it arrives. There is no need to preserve a pre-existing computational context because either the context is irrelevant or the target machine's context is exactly what we want.

AspectDescription
What movesOnly the code (plus optional initialisation data).
What staysExecution state, resource bindings.
RequirementThe target machine must be able to execute the code (compatible runtime).
ComplexityLow — no need to capture, serialise, or restore execution state.
Canonical examplesJava Applets, JavaScript fetched by a browser, remote evaluation in RPC-based systems.
Trade-off

Weak mobility is easy to implement but limited in expressiveness. You cannot suspend a computation on machine A and resume it on machine B — the code always restarts. For iterative, stateful computations that need to roam (like a mobile agent bargaining across multiple marketplaces), weak mobility is insufficient.

5. Strong Mobility: Moving the Execution Context

Strong mobility transfers both the code segment and the execution segment. A process can be stopped at an arbitrary point, its entire execution state captured (stack, program counter, private data), shipped to another machine, and then resumed exactly where it left off.

The benefit is transformational: a single logical thread of control can physically traverse multiple machines, accumulating state and making decisions along the way, without ever losing its place. This is the foundation of the mobile agent paradigm.

AspectDescription
What movesCode segment + execution segment (stack, PC, private data).
What staysResource segment (bindings must be re-established or adapted).
RequirementThe middleware or runtime must support capturing and restoring execution state — this is demanding.
ComplexityHigh — requires language-level or VM-level support (e.g., continuations, checkpoint/restart).
Canonical examplesMobile agents (e.g., Aglets, Agent Tcl), process migration in MOSIX, VM live migration.
Editor's note

Strong mobility is rare in mainstream platforms precisely because of the implementation burden. Languages like Java would need VM support to capture the full stack and heap state. In practice, many systems that claim "agent" capabilities actually implement weak mobility — the agent carries a program counter variable and uses a dispatch loop to simulate resumption, rather than truly capturing the native stack.

The examiner may ask

"Why is strong mobility difficult to implement in practice? What runtime support does it require?" Be prepared to discuss stack capture, serialisation of closures, heterogeneous architectures, and the security implications of accepting foreign execution state.

6. Mobility Models: The Four Paradigms

Based on who initiates the migration (sender or receiver) and what is transferred (code only, or code plus execution state), we can classify distributed systems into four canonical mobility models. This taxonomy comes from Tanenbaum & van Steen (2017) and is essential for reasoning about any system that involves code movement.

ModelInitiatorMobilityWhat travelsExamples
Client-Server (CS)N/ANoneData only (requests and replies)HTTP, SQL queries, traditional RPC
Code-on-Demand (COD)ReceiverWeakCode (know-how)Java Applets, JavaScript in browsers
Remote Evaluation (REV)SenderWeakCode (know-how)Stored procedures, serverless functions, eval servers
Mobile Agents (MA)SenderStrongCode + execution stateAglets, Agent Tcl, Web crawlers with state

The key difference between client-server and the other three is that in CS, the code that executes at the server is already there — only data flows. In COD, REV, and MA, the know-how (the program itself) is transferred across the network.

The difference between COD and REV is direction: COD is pulled by the receiver ("give me the code to execute"), while REV is pushed by the sender ("here, execute this code for me"). MA adds the execution segment on top of REV's push model.

7. Interactive Mobility Model Explorer

Explore the four canonical mobility models. Each state represents a paradigm; transitions show how adding mobility (weak or strong) and changing the initiator (sender vs receiver) moves you from one model to the next.

8. Sender-Initiated vs Receiver-Initiated Migration

The initiator of code migration has profound implications for the interaction model, the security requirements, and the complexity of the overall system.

Sender-Initiated Migration

Migration is triggered by the machine where the code currently resides or executes. The sender pushes code (and possibly execution state) to a target machine.

  • Examples: Search bots, mobile agents, remote evaluation (REV).
  • Security burden: The target must protect its resources from potentially malicious incoming code. Servers need to authenticate senders and sandbox the executing code.
  • Complexity: Higher — the interaction scheme is more involved because the sender must know about the receiver (its address, its capabilities, its interface).
  • Use when: You want to proactively distribute computation, explore remote resources, or execute a task across multiple sites sequentially.

Receiver-Initiated Migration

Migration is triggered by the target machine, which requests new behaviour from a code source (typically a server). The receiver pulls code toward itself.

  • Examples: Java Applets, JavaScript chunks loaded by a browser.
  • Security burden: The client (receiver) needs to protect itself, but the attack surface is limited — only a few client-side resources are exposed. The server side is largely unaffected.
  • Complexity: Lower — clients may be anonymous; the server does not need to track them. The interaction is typically a simple request-response for code.
  • Use when: You want to enrich a client with capabilities that the server defines, without the server needing to track individual client state.
The examiner may ask

"Compare sender-initiated and receiver-initiated migration in terms of security, anonymity, and the complexity of the interaction scheme. Give a concrete example for each."

9. Separate vs Target Process Execution

When weak mobility delivers code to a machine, a design decision must be made: should the incoming code execute in the same process as the receiver, or in a separate, isolated process?

StrategyHow it worksProsCons
Target process executionThe mobile code runs directly in the address space of the receiving process (e.g., a browser's rendering engine running JavaScript).No inter-process communication overhead; direct access to the host's data structures; simpler integration.Malicious or buggy code can corrupt the host process; a crash in the mobile code can bring down the host.
Separate process executionThe mobile code is assigned to a dedicated process with its own address space; communication with the host happens via IPC.Strong isolation — a crash or exploit in the mobile code cannot directly compromise the host; resource usage can be capped.IPC overhead; more complex to set up and manage; sharing state requires explicit marshalling.

The Java Applet model originally ran in the browser's process (target process), which led to numerous security vulnerabilities. Modern browsers isolate each tab in a separate process and use sandboxing techniques, implementing a hybrid approach: separate-process execution with controlled, mediated access to host resources.

Key idea

The choice between target-process and separate-process execution is fundamentally a trade-off between performance and protection. It is the same tension that appears in microkernel vs monolithic kernel design, and in container vs VM isolation.

10. Cloning as an Alternative to Migration

Strong mobility can also be supported through remote cloning: instead of moving a process, you create an exact copy of it on a remote machine. The original process continues to run on the source machine while the clone executes in parallel on the target.

Cloning differs from migration in several important ways:

AspectMigrationCloning
Original processStops on source; resumes on target.Continues running on source.
Number of instancesExactly one at any time.Two (or more) running in parallel.
Distribution transparencyPartial — process moves, but clients must know where it is.Improved — replicated instances can serve requests from multiple locations.
Classic exampleMOSIX process migration.UNIX fork() followed by remote execution of the child.
Editor's note

Cloning improves distribution transparency because the replicated processes can appear as a single logical service to clients, with requests routed to the nearest or least-loaded replica. This idea underlies modern patterns like function replication in edge computing and actor-model systems (e.g., Akka cluster sharding).

11. The Resource Segment: Why Resources Resist Movement

So far we have discussed moving the code segment and the execution segment. But a process does not run in a vacuum — it depends on external resources: files, databases, printers, network sockets, other processes. These are referenced through the resource segment.

Resources pose two fundamental challenges during migration:

  1. Resources may be impossible to move: A huge database cannot realistically be shipped across the network. A printer is physically bound to a specific machine. A GPU is not portable. Either references must be updated to point to equivalent resources at the target, or the resource itself must be moved — if that is feasible.
  2. References may break: A file descriptor valid on machine A is meaningless on machine B. A URL pointing to a local service on the source machine will not resolve at the destination. Bindings must be re-established.
Key idea

The resource segment is the hardest part of code mobility. Code and execution state are digital and relatively small; resources are often large, physically constrained, or externally controlled. The taxonomy of resource bindings (next two sections) provides a framework for reasoning about what actions are needed when code moves.

12. Process-to-Resource Binding Types

The first dimension of the resource problem is: how does the migrating process refer to its resources? There are three binding types:

Binding typeMeaningExampleMigration implication
By identifierThe process needs a resource with a specific name or address.A URL (https://api.example.com/v1), a DNS name, a local file path, a process ID.If the identifier is a global name (URL), it may still work at the target. If it is local (PID, local path), it will break and must be rebound.
By valueThe process needs a resource based on its content, not its identity.A specific code library (e.g., "I need NumPy v1.24"), a configuration file with known content.The resource can potentially be copied or substituted with an equivalent one at the target, as long as the value matches.
By typeThe process needs a resource of a given category, any instance will do.A printer, a display, a GPU, a network interface.The target machine must provide a resource of the same type. The binding can be re-established locally if such a resource exists.
The examiner may ask

"Given a process that opens a local SQLite database file, downloads a Python library, and renders output to a screen, classify each resource dependency by binding type and explain the migration implications."

13. Resource-to-Machine Binding Types

The second dimension is: how tightly is the resource bound to the machine that currently hosts it? This determines whether the resource can be moved at all, and at what cost:

Binding typeMeaningExamplesCan it move?
UnattachedResources that can be easily moved between machines with minimal cost.Files associated with the migrating code (a configuration file, a log file), cached data.Yes — they can be transferred along with the code, or fetched on demand.
FastenedResources that could theoretically be moved, but at a significant cost (size, bandwidth, downtime).A local database, a large dataset, a Docker image.Possible but expensive — often better to leave it in place and access it remotely, or replicate it lazily.
FixedResources intrinsically bound to a specific machine or physical location.A monitor, a printer, a specialised hardware device, a GPS receiver.No — the resource cannot move. The process must either give it up or rebind to an equivalent at the target.
Important

The 3x3 matrix formed by process-to-resource binding (identifier/value/type) and resource-to-machine binding (unattached/fastened/fixed) defines, for each combination, the set of actions the migration system must take. This is the subject of the next two sections.

14. Migration Patterns: Annotated Code

The following pseudocode illustrates the four canonical mobility patterns. Click each line to reveal its explanation.

15. Actions on Resources During Migration

When code migrates to a new machine, the references in its resource segment must be adapted. Tanenbaum & van Steen (2017) identify the actions to be taken for each combination of process-to-resource binding and resource-to-machine binding. The following table summarises the decision matrix:

Resource-to-machine
→ Process-to-resource ↓
UnattachedFastenedFixed
By identifierMove the resource (MV) or rebind to a global reference (GR).Rebind to a global reference (GR) — the resource stays but must be reachable via a network identifier.Rebind to a local resource of the same type at the target (GR) — or give up the resource.
By valueCopy the resource value (CP) to the target.Copy the resource (CP) if feasible; otherwise establish a remote reference.Copy is meaningless for fixed resources; rebind to an equivalent at the target (GR).
By typeRebind to a resource of the same type at the target (GR).Rebind to a local resource of the same type (GR) or access the fastened one remotely.Rebind to a local resource of the same type at the target (GR).

The possible actions referenced above:

16. Interactive Resource-Action Explorer

Use the explorer below to understand which migration action is appropriate for each combination of binding types. Click any cell in the matrix to see a concrete scenario.

17. Summary

  1. 1. Motivation

    Code mobility is needed when passing data is not enough — for load balancing, minimising communication, improving scalability, enabling dynamic reconfiguration, and increasing fault tolerance.

  2. 2. Three segments

    A process decomposes into code, execution, and resource segments. Which segments travel determines the class of mobility: weak (code only) or strong (code + execution).

  3. 3. Weak mobility

    Only the code segment moves. The program restarts from the beginning at the destination. Simple to implement, sufficient for code-on-demand and remote evaluation.

  4. 4. Strong mobility

    Both code and execution state travel. The process suspends, moves, and resumes exactly where it left off. Demanding to implement, but enables mobile agents.

  5. 5. Four paradigms

    Client-Server (no mobility), Code-on-Demand (receiver-initiated, weak), Remote Evaluation (sender-initiated, weak), Mobile Agents (sender-initiated, strong).

  6. 6. Initiation

    Sender-initiated migration is more complex (targets need heavy security) but enables proactive distribution. Receiver-initiated is simpler (anonymous clients, lightweight security) but the server retains control.

  7. 7. Resources

    The resource segment is the hardest challenge — bindings can be by identifier, value, or type; resources can be unattached, fastened, or fixed. The 3x3 matrix determines whether to move, copy, or rebind.

Check Your Understanding

Define weak mobility and strong mobility. What travels with the code in each case?

Weak mobility transfers only the code segment (plus optional initialisation data). The code starts execution from the beginning at the destination. Strong mobility transfers both the code segment and the execution segment (private data, stack, program counter), allowing the process to resume exactly where it was suspended. The key distinction is whether the execution state is captured and restored.

Classify each scenario into one of the four paradigms (CS, COD, REV, MA): (a) a browser requests a JavaScript file from a server; (b) a user calls a stored procedure on a PostgreSQL database; (c) a web crawler that visits multiple sites, each time carrying its index of already-visited URLs; (d) a traditional HTTP GET request.

(a) Code-on-Demand (COD) — receiver (browser) pulls code. (b) Remote Evaluation (REV) — sender (client) pushes code to be executed remotely. (c) Mobile Agents (MA) — the crawler is a sender-initiated agent carrying both code and execution state (the visited-URL index) across sites. (d) Client-Server (CS) — no code mobility, only data (the request and the response) flows.

Why is receiver-initiated migration generally considered less complex than sender-initiated migration? Discuss security and anonymity.

In receiver-initiated migration, the target initiates the transfer — it asks for code and knows what it is getting. Only a few client-side resources need protection (sandboxing the incoming code). Clients can remain anonymous — the server does not need to track who downloaded code. In sender-initiated migration, the target receives code it did not ask for, and must authenticate the sender, verify the code's integrity, and protect its resources from potentially malicious behaviour. The server must know about clients, making the interaction more complex.

A process opens a local SQLite file, imports a Python library by name, and writes to a physically connected printer. For each resource, state: (i) process-to-resource binding type; (ii) resource-to-machine binding type; (iii) the likely migration action needed.

SQLite file: (i) Binding by identifier (local file path). (ii) Unattached or fastened depending on size. (iii) If unattached, MV (move the file). If fastened, GR (rebind to a network-accessible copy or copy lazily). Python library: (i) Binding by value (the library's content, identified by name and version). (ii) Unattached. (iii) CP (copy/install the library at the target). Printer: (i) Binding by type (any printer will do, or by identifier if a specific named printer). (ii) Fixed. (iii) GR (rebind to a printer available at the target machine).

Explain why cloning can improve distribution transparency compared to migration.

With migration, a single process instance moves from one machine to another; clients must track its current location to reach it. With cloning, multiple replicas of the same process exist simultaneously on different machines. Clients can be routed to the nearest replica, and the system can present a single logical service endpoint. This transparent replication hides the physical distribution from clients — they do not need to know which replica serves their request, and failover to another replica is seamless if one fails.

Describe a situation where weak mobility is sufficient and strong mobility would be overkill. Then describe the opposite.

Weak mobility sufficient: A serverless function (e.g., AWS Lambda) — the code is stateless and each invocation is independent; the platform just needs the function code and input parameters. There is no execution state to carry. Strong mobility necessary: A negotiation agent that visits multiple e-commerce sites, gathers quotes, and makes a decision based on accumulated information. The agent must remember which sites it visited and what offers it received; suspending and resuming its decision logic across sites requires carrying the execution state.

What are the three possible actions (MV, CP, GR) a migration system can take regarding a resource when code moves? Give a concrete example of when each is used.

MV (Move): Transfer the resource itself. Example: a small configuration file specific to the migrating process is sent along with the code to the target. CP (Copy): Create a copy of the resource at the target; the original stays. Example: copying a read-only dataset that the process needs at both locations. GR (Global Rebind): Update the reference to point to a globally reachable resource or a local equivalent. Example: a process that used a local database on the source machine rebinds to an equivalent database instance accessible at the target site.