By many different methods, not easily reduced to just a few paras. Here are some, in roughly ascending order of abstraction.
- The OS implements one or more privileged instructions (trap, syscall, etc). The compiler emits code to translate certain language constructs into those instructions. [ASM]
- The OS provides an API in a form compatible with the external interfaces of a specific target language (one that ordinarily provides a mechanism for calling separately compiled translation units). Calls to the OS are indistinguishable from calls to user code. [C]
- The OS provides binding layers in a form intended to be compatible with the external interfaces of a specific various target languages. Calls to the OS are similar to calls to user code. [C++, many]
- The OS implements an API in a form designed to be callable from various languages but native to none, along with binding layers. This mechanism may also be used for calls to user code. [VB, COM]
- The OS provides a 'framework' with an extensive range of capabilities, in lieu of direct access to its services. The compiler usually provides privileged access to the framework, as well as allowing calls that are indistinguishable from user code. [C#, Java, Objective-C*]
These mechanisms are often layered. The calls at a higher layer are translated into calls at a lower layer behind the scenes. Objective-C is an odd one, in providing two different API mechanisms.
At a physical deployment level, the services can be provided by:
- Generated code 'traps' directly into the OS, often provided as macros.
- OS library code is statically linked (LIB), often provided as 'header' files.
- OS library code is dynamically linked using low level mechanisms similar to those used by the OS itself, often via headers and an intervening static lib (DLL, SO).
- OS library code is dynamically linked using a mechanism that is part of the API specification, using mechanisms specific to the spec (COM, .NET)
Wherever two disparate technologies (such as Ruby and Cocoa) are able to interact, there is usually a binding layer created by a tool, or manually. Eventually it will use one of the mechanisms above (unless I've missed something).