Perché è specifico del sistema operativo del software?


77

Sto cercando di determinare i dettagli tecnici del perché il software prodotto utilizzando linguaggi di programmazione per determinati sistemi operativi funziona solo con loro.

Comprendo che i binari sono specifici di determinati processori a causa del linguaggio macchina specifico del processore che comprendono e dei diversi set di istruzioni tra processori diversi. Ma da dove viene la specificità del sistema operativo? Supponevo che fossero le API fornite dal sistema operativo, ma poi ho visto questo diagramma in un libro: Diagramma

Sistemi operativi - Principi di progettazione e interni 7 ° ed - W. Stallings (Pearson, 2012)

Come puoi vedere, le API non sono indicate come parte del sistema operativo.

Se per esempio costruisco un semplice programma in C usando il seguente codice:

#include<stdio.h>

main()
{
    printf("Hello World");

}

Il compilatore sta facendo qualcosa di specifico del sistema operativo durante la compilazione?


15
Stampa su una finestra? o una console? o alla memoria grafica? Come si inseriscono i dati lì? Guardando printf per Apple] [+ sarebbe leggermente diverso rispetto a un Mac OS 7 e di nuovo abbastanza diverso da Mac OS X (solo con una "linea" di computer).

3
Perché se scrivessi quel codice per Mac OS 7, verrebbe mostrato nel testo in una nuova finestra. Se lo facessi su Apple] [+, scriverebbe direttamente su un segmento di memoria. Su Mac OS X, lo scrive su una console. Pertanto, sono tre diversi modi di scrivere che gestiscono il codice in base all'hardware di esecuzione gestito dal livello della libreria.

2
@StevenBurnap yep - en.wikipedia.org/wiki/Aztec_C

10
La tua funzione FFT funzionerà felicemente su Windows o Linux (sulla stessa CPU), senza nemmeno ricompilare. Ma allora come hai intenzione di visualizzare il risultato? Utilizzando un'API del sistema operativo, ovviamente. ( printfda msvcr90.dll non è lo stesso printfdi libc.so.6)
user253751

9
Anche se le API "non fanno parte del sistema operativo", sono comunque diverse se si passa da un sistema operativo all'altro. (Il che, naturalmente, solleva la questione di cosa significhi davvero la frase "non parte del sistema operativo", secondo il diagramma.)
Theodoros Chatzigiannakis

Risposte:


78

Fai riferimento a come se il codice è specifico per una CPU, perché deve essere specifico anche per un sistema operativo. Questa è in realtà una domanda più interessante che molte delle risposte qui hanno assunto.

Modello di sicurezza della CPU

Il primo programma eseguito sulla maggior parte delle architetture della CPU viene eseguito all'interno di quello che viene chiamato l'anello interno o l'anello 0 . Il modo in cui uno specifico arco della CPU implementa gli anelli varia, ma risulta che quasi tutte le moderne CPU hanno almeno 2 modalità di funzionamento, una con privilegi e che esegue un codice 'bare metal' che può eseguire qualsiasi operazione legale che la CPU può eseguire e l'altra è non attendibile ed esegue codice protetto che può eseguire solo un set definito di funzionalità sicure. Tuttavia, alcune CPU hanno una granularità molto più elevata e per utilizzare le VM in modo sicuro sono necessari almeno 1 o 2 anelli extra (spesso etichettati con numeri negativi), ma questo va oltre lo scopo di questa risposta.

Dove arriva il sistema operativo

Primi SO a tasking singolo

Nei primissimi DOS e altri primi sistemi basati su tasking singolo tutto il codice veniva eseguito nell'anello interno, ogni programma che avessi mai eseguito aveva il pieno potere sull'intero computer e poteva fare letteralmente qualsiasi cosa se si comportava male, inclusa la cancellazione di tutti i tuoi dati o persino il danneggiamento dell'hardware in alcuni casi estremi come l'impostazione di modalità di visualizzazione non valide su schermi di visualizzazione molto vecchi, peggio, ciò potrebbe essere causato semplicemente da un codice errato senza malizia.

Questo codice era in gran parte indipendente dal sistema operativo, a condizione che tu avessi un caricatore in grado di caricare il programma in memoria (abbastanza semplice per i primi formati binari) e il codice non si basava su alcun driver, implementando tutto l'accesso hardware stesso dovrebbe essere eseguito qualsiasi sistema operativo purché sia ​​eseguito nell'anello 0. Nota, un sistema operativo molto semplice come questo è solitamente chiamato monitor se viene semplicemente utilizzato per eseguire altri programmi e non offre alcuna funzionalità aggiuntiva.

Moderni sistemi operativi multitasking

I sistemi operativi più moderni tra cui UNIX , le versioni di Windows che iniziano con NT e vari altri sistemi operativi ora oscuri hanno deciso di migliorare questa situazione, gli utenti volevano funzionalità aggiuntive come il multitasking in modo da poter eseguire più di un'applicazione contemporaneamente e protezione, quindi un bug ( o codice dannoso) in un'applicazione non potrebbe più causare danni illimitati alla macchina e ai dati.

Ciò è stato fatto usando gli anelli di cui sopra, il sistema operativo avrebbe avuto l'unico posto in esecuzione nell'anello 0 e le applicazioni sarebbero state eseguite negli anelli esterni non attendibili, in grado di eseguire solo un insieme limitato di operazioni consentite dal sistema operativo.

Tuttavia, questa maggiore utilità e protezione ha avuto un costo, i programmi ora dovevano lavorare con il sistema operativo per eseguire attività che non potevano svolgere autonomamente, ad esempio non potevano più assumere il controllo diretto sul disco rigido accedendo alla sua memoria e modificando arbitrariamente dati, invece hanno dovuto chiedere al sistema operativo di eseguire queste attività per loro in modo che potesse verificare che fossero autorizzati a eseguire l'operazione, non modificando i file che non appartenevano a loro, avrebbe anche verificato che l'operazione fosse effettivamente valida e non lascerebbe l'hardware in uno stato indefinito.

Ciascun sistema operativo ha deciso un'implementazione diversa per queste protezioni, in parte in base all'architettura per la quale il sistema operativo è stato progettato e in parte basato sulla progettazione e sui principi del sistema operativo in questione, UNIX, ad esempio, ha posto l'accento sulle macchine utili per l'uso multiutente e focalizzate le funzionalità disponibili per questo mentre Windows è stato progettato per essere più semplice, per funzionare su hardware più lento con un singolo utente. Il modo in cui i programmi di spazio utente parlano anche con il sistema operativo è completamente diverso su X86 come sarebbe su ARM o MIPS, ad esempio, costringendo un sistema operativo multipiattaforma a prendere decisioni basate sulla necessità di lavorare sull'hardware a cui è destinato.

Queste interazioni specifiche del sistema operativo sono generalmente chiamate "chiamate di sistema" e comprendono il modo in cui un programma di spazio utente interagisce completamente con l'hardware attraverso il sistema operativo, sostanzialmente differiscono in base alla funzione del sistema operativo e quindi un programma che fa il suo lavoro attraverso le chiamate di sistema deve essere specifico del sistema operativo.

Il programma di caricamento

Oltre alle chiamate di sistema, ciascun sistema operativo fornisce un metodo diverso per caricare un programma dal supporto di memoria secondario e nella memoria , per essere caricabile da un sistema operativo specifico il programma deve contenere un'intestazione speciale che descriva al sistema operativo come potrebbe essere caricato ed eseguito.

Questa intestazione era abbastanza semplice che scrivere un caricatore per un formato diverso era quasi banale, tuttavia con formati moderni come elfo che supportano funzionalità avanzate come collegamenti dinamici e dichiarazioni deboli è ora quasi impossibile per un sistema operativo tentare di caricare binari che non sono stati progettati per questo, questo significa che, anche se non ci sono incompatibilità di chiamate di sistema, è immensamente difficile persino posizionare un programma in ram in un modo in cui può essere eseguito.

biblioteche

I programmi raramente usano le chiamate di sistema direttamente, tuttavia ottengono quasi esclusivamente la loro funzionalità anche se le librerie che avvolgono le chiamate di sistema in un formato leggermente più amichevole per il linguaggio di programmazione, ad esempio, C ha la libreria standard C e glibc sotto Linux e librerie simili e win32 sotto Windows NT e versioni successive, la maggior parte degli altri linguaggi di programmazione hanno anche librerie simili che avvolgono la funzionalità del sistema in modo appropriato.

Queste librerie possono persino superare i problemi multipiattaforma come sopra descritto, esistono una serie di librerie progettate per fornire una piattaforma uniforme alle applicazioni e gestire internamente le chiamate a una vasta gamma di sistemi operativi come SDL , ciò significa che sebbene i programmi non possono essere compatibili con i binari, i programmi che usano queste librerie possono avere un sorgente comune tra le piattaforme, rendendo il porting semplice come la ricompilazione.

Eccezioni a quanto sopra

Nonostante tutto quello che ho detto qui, ci sono stati tentativi di superare i limiti di non essere in grado di eseguire programmi su più di un sistema operativo. Alcuni buoni esempi sono il progetto Wine che ha emulato con successo sia il caricatore del programma win32, il formato binario e le librerie di sistema che consentono ai programmi Windows di essere eseguiti su vari UNIX. Esiste anche un livello di compatibilità che consente a diversi sistemi operativi BSD UNIX di eseguire software Linux e, naturalmente, lo spessore di Apple che consente di eseguire il vecchio software MacOS su MacOS X.

Tuttavia, questi progetti funzionano attraverso enormi livelli di sforzo di sviluppo manuale. A seconda della differenza tra i due sistemi operativi, la difficoltà varia da uno spessore abbastanza piccolo all'emulazione quasi completa dell'altro sistema operativo, che è spesso più complessa della scrittura di un intero sistema operativo in sé e quindi questa è l'eccezione e non la regola.


6
+1 "Perché è specifico del sistema operativo del software?" Perché la storia.
Paul Draper,

2
il modello di sicurezza della CPU ha origine x86? perché e quando è stato inventato il modello?
n611x007,

8
@naxa No, è da molto tempo precedente a x86, è stato parzialmente implementato per Multics nel 1969, che è il primo sistema operativo con utili funzioni multiutente per la condivisione del tempo che richiedono questo modello nel computer GE-645 , tuttavia questa implementazione era incompleta e si basava su supporto software, la prima implementazione completa e sicura in hardware è stata nel suo successore, Honeywell 6180 . Questo era completamente basato su hardware e consentiva a Multics di eseguire codice da più utenti senza la possibilità di interferire.
Valità,

@Vality Inoltre, IBM LPAR è ~ 1972.
Elliott Frisch,

@ElliottFrisch wow, è impressionante. Non mi ero reso conto che era così presto. Grazie per quelle informazioni.
Valenza,

48

Come puoi vedere, le API non sono indicate come parte del sistema operativo.

Penso che stai leggendo troppo nel diagramma. Sì, un sistema operativo specificherà un'interfaccia binaria per il modo in cui vengono chiamate le funzioni del sistema operativo e definirà anche un formato di file per gli eseguibili, ma fornirà anche un'API, nel senso di fornire un catalogo di funzioni che possono essere chiamate da un'applicazione per invocare servizi OS.

Penso che il diagramma stia solo cercando di enfatizzare che le funzioni del sistema operativo sono generalmente invocate attraverso un meccanismo diverso rispetto a una semplice chiamata in libreria. La maggior parte del sistema operativo comune utilizza gli interruttori del processore per accedere alle funzioni del sistema operativo. I moderni sistemi operativi tipici non consentono a un programma utente di accedere direttamente a qualsiasi hardware. Se vuoi scrivere un personaggio sulla console, dovrai chiedere al sistema operativo di farlo per te. La chiamata di sistema utilizzata per scrivere sulla console varierà da SO a SO, quindi esiste un esempio del perché il software è specifico del SO.

printf è una funzione della libreria di runtime C e in un'implementazione tipica è una funzione abbastanza complessa. Se vai su Google puoi trovare la fonte per diverse versioni online. Vedi questa pagina per una visita guidata di uno . Giù nell'erba, anche se finisce per effettuare una o più chiamate di sistema, e ognuna di quelle chiamate di sistema è specifica per il sistema operativo host.


4
E se tutto il programma facesse era aggiungere due numeri, senza input o output. Quel programma sarebbe ancora specifico del sistema operativo?
Paul,

2
I sistemi operativi intendono mettere la maggior parte delle cose specifiche dell'hardware dietro / in un livello di astrazione. Tuttavia, il sistema operativo stesso (l'astrazione) può differire da implementazione a implementazione. C'è POSIX che alcuni sistemi operativi (più o meno) aderiscono e forse alcuni altri, ma i sistemi operativi globali semplicemente differiscono troppo nella loro parte "visibile" dell'astrazione. Come detto prima: non puoi aprire / home / user su windows e non puoi accedere a HKEY_LOCAL_MACHINE \ ... su un sistema * N * X. È possibile scrivere software virtuale ( "emulazione") per questo per contribuire a portare questi sistemi più vicino insieme, ma che sarà sempre "3a parte" (da OS POV).
RobIII,

16
@Paul Sì. In particolare, il modo in cui è impacchettato in un eseguibile sarebbe specifico del sistema operativo.
Smetti di fare del male a Monica l'

4
@TimSeguine Non sono d'accordo con il tuo esempio di XP contro 7. Gran parte del lavoro è svolto da Microsoft per garantire l'esistenza della stessa API in 7 come in XP. Chiaramente quello che è successo qui è che il programma è stato progettato per funzionare con una determinata API o contratto. Il nuovo sistema operativo ha appena aderito alla stessa API / contratto. Nel caso di Windows, tuttavia, l'API è molto proprietaria, motivo per cui nessun altro fornitore di sistema operativo la supporta. Anche allora ci sono molti esempi di programmi che NON funzionano il 7.
ArTs

3
@Paul: un programma che non ha input / output è il programma vuoto , che dovrebbe essere compilato come no-op.
Bergi,

14

Il compilatore sta facendo qualcosa di specifico del sistema operativo durante la compilazione?

Probabilmente. Ad un certo punto durante il processo di compilazione e collegamento, il codice viene trasformato in un file binario specifico del sistema operativo e collegato a tutte le librerie richieste. Il programma deve essere salvato in un formato previsto dal sistema operativo in modo che il sistema operativo possa caricare il programma e iniziare a eseguirlo. Inoltre, stai chiamando la funzione di libreria standard printf(), che a un certo livello è implementata in termini di servizi forniti dal sistema operativo.

Le librerie forniscono un'interfaccia - uno strato di astrazione dal sistema operativo e dall'hardware - e che consente di ricompilare il programma per un sistema operativo diverso o hardware diverso. Ma quell'astrazione esiste a livello di sorgente: una volta che il programma è stato compilato e collegato, è collegato a un'implementazione specifica di quell'interfaccia che è specifica per un determinato sistema operativo.


12

Esistono diversi motivi, ma uno dei motivi molto importanti è che il sistema operativo deve sapere come leggere la serie di byte che compongono il programma in memoria, trovare le librerie associate a quel programma e caricarli in memoria, e quindi iniziare a eseguire il codice del programma. Per fare ciò, i creatori del sistema operativo creano un formato particolare per quella serie di byte in modo che il codice del sistema operativo sappia dove cercare le varie parti della struttura del programma. Poiché i principali sistemi operativi hanno autori diversi, questi formati spesso hanno poco a che fare l'uno con l'altro. In particolare, il formato eseguibile di Windows ha poco in comune con il formato ELF più varianti di Unix uso. Quindi tutto questo caricamento, collegamento dinamico ed esecuzione del codice deve essere specifico del sistema operativo.

Successivamente, ciascun sistema operativo fornisce un diverso set di librerie per comunicare con il livello hardware. Queste sono le API che menzioni e in genere sono librerie che presentano un'interfaccia più semplice per lo sviluppatore mentre la traducono in chiamate più complesse e più specifiche nelle profondità del sistema operativo stesso, spesso chiamate non documentate o protette. Questo livello è spesso piuttosto grigio, con le nuove API "OS" sono costruite parzialmente o interamente su API più vecchie. Ad esempio, in Windows, molte delle più recenti API che Microsoft ha creato negli anni sono essenzialmente strati sopra le API Win32 originali.

Un problema che non si presenta nel tuo esempio, ma che è uno dei più grandi che gli sviluppatori devono affrontare è l'interfaccia con il gestore delle finestre, per presentare una GUI. Il fatto che il gestore delle finestre faccia parte del "sistema operativo" a volte dipende dal tuo punto di vista, così come dal sistema operativo stesso, con la GUI in Windows integrata con il sistema operativo a un livello più profondo, mentre le GUI su Linux e OS X sono più direttamente separati. Questo è molto importante perché oggi ciò che la gente chiama in genere "Il sistema operativo" è una bestia molto più grande di ciò che i libri di testo tendono a descrivere, in quanto include molti, molti componenti a livello di applicazione.

Infine, non strettamente un problema del sistema operativo, ma un aspetto importante nella generazione di file eseguibili è che macchine diverse hanno destinazioni di linguaggio assembly diverse, e quindi il codice oggetto generato effettivo deve essere diverso. Questo non è strettamente un problema "OS" ma piuttosto un problema hardware, ma significa che avrai bisogno di build diverse per piattaforme hardware diverse.


2
Potrebbe essere utile notare che i formati eseguibili più semplici possono essere caricati utilizzando solo una piccola quantità di RAM (se presente) oltre a quella richiesta per contenere il codice caricato, mentre formati più complessi possono richiedere un ingombro RAM molto maggiore durante e, in alcuni casi, anche dopo, caricamento. MS-DOS carica i file COM fino a 63,75 KB semplicemente leggendo i byte sequenziali nella RAM a partire dall'offset 0x100 di un segmento arbitrario, carica CX con l'indirizzo finale e passa a quello. Compilazione single-pass potrebbe essere realizzata senza back-patching (utile con floppy) da ...
supercat

1
... avendo il compilatore includa con ogni routine un elenco di tutti i punti di patch, ognuno dei quali includerebbe l'indirizzo del precedente elenco e ponendo l'indirizzo dell'ultimo elenco alla fine del codice. Il sistema operativo carica semplicemente il codice come byte non elaborati, ma una piccola routine all'interno del codice potrebbe applicare tutte le patch di indirizzo necessarie prima di eseguire la parte principale del codice.
supercat

9

Da un'altra mia risposta :

Prendi in considerazione le prime macchine DOS e quale fu il reale contributo di Microsoft al mondo:

Autocad ha dovuto scrivere i driver per ogni stampante su cui poteva stampare. Anche il loto 1-2-3. In effetti, se volevi stampare il tuo software, dovevi scrivere i tuoi driver. Se c'erano 10 stampanti e 10 programmi, allora 100 pezzi diversi dello stesso codice dovevano essere scritti separatamente e indipendentemente.

Ciò che Windows 3.1 ha cercato di realizzare (insieme a GEM e così tanti altri livelli di astrazione) è farlo in modo che il produttore della stampante abbia scritto un driver per la propria stampante e il programmatore abbia scritto un driver per la classe di stampanti Windows.

Ora con 10 programmi e 10 stampanti, devono essere scritti solo 20 pezzi di codice e poiché il lato microsoft del codice era lo stesso per tutti, allora gli esempi di MS significavano che avevi pochissimo lavoro da fare.

Ora un programma non era limitato alle sole 10 stampanti che avevano scelto di supportare, ma a tutte le stampanti i cui produttori fornivano i driver per Windows.

Quindi il sistema operativo fornisce servizi alle applicazioni in modo che le applicazioni non debbano svolgere attività ridondanti.

Il tuo programma C di esempio utilizza printf, che invia i caratteri a stdout, una risorsa specifica del sistema operativo che visualizzerà i caratteri su un'interfaccia utente. Il programma non ha bisogno di sapere dove si trova l'interfaccia utente: potrebbe essere in DOS, potrebbe essere in una finestra grafica, potrebbe essere reindirizzato a un altro programma e utilizzato come input per un altro processo.

Poiché il sistema operativo fornisce queste risorse, i programmatori possono ottenere molto di più con poco lavoro.

Tuttavia, anche l'avvio di un programma è complicato. Il sistema operativo si aspetta che all'inizio un file eseguibile contenga determinate informazioni che indicano al sistema operativo come dovrebbe essere avviato e, in alcuni casi (ambienti più avanzati come Android o iOS), quali risorse saranno necessarie e che richiedono l'approvazione poiché toccano risorse esterne al "sandbox": una misura di sicurezza che aiuta a proteggere gli utenti e altre app da programmi che si comportano in modo inappropriato.

Pertanto, anche se il codice macchina eseguibile è lo stesso e non sono necessarie risorse del sistema operativo, un programma compilato per Windows non verrà eseguito su un sistema operativo OS X senza un ulteriore livello di emulazione o traduzione, anche sullo stesso hardware esatto.

I primi sistemi operativi in ​​stile DOS potevano spesso condividere programmi, poiché implementavano la stessa API nell'hardware (BIOS) e il sistema operativo collegato all'hardware per fornire servizi. Quindi, se hai scritto e compilato un programma COM - che è solo un'immagine di memoria di una serie di istruzioni del processore - potresti eseguirlo su CP / M, MS-DOS e molti altri sistemi operativi. In effetti è ancora possibile eseguire programmi COM su macchine Windows moderne. Altri sistemi operativi non usano gli stessi hook API BIOS, quindi i programmi COM non verranno eseguiti su di essi senza, ancora una volta, un livello di emulazione o traduzione. I programmi EXE seguono una struttura che include molto più che semplici istruzioni per i processori e quindi, insieme ai problemi dell'API, non verrà eseguito su un computer che non capisce come caricarlo in memoria ed eseguirlo.


7

In realtà, la vera risposta è che se ogni sistema operativo comprendesse lo stesso layout di file binario eseguibile e si limitasse a funzioni standardizzate (come nella libreria standard C) fornite dal sistema operativo (che i sistemi operativi forniscono), il software sarebbe , infatti, funziona su qualsiasi sistema operativo.

Certo, la realtà è che non è così. Un EXEfile non ha lo stesso formato di un ELFfile, anche se entrambi contengono un codice binario per la stessa CPU. * Quindi ogni sistema operativo dovrebbe essere in grado di interpretare tutti i formati di file e semplicemente non lo fanno nel all'inizio, e non c'era motivo per loro di iniziare a farlo in seguito (quasi certamente per motivi commerciali piuttosto che tecnici).

Inoltre, il tuo programma probabilmente deve fare cose che la libreria C non definisce come fare (anche per cose semplici come elencare il contenuto di una directory), e in quei casi ogni sistema operativo fornisce le sue funzioni per raggiungere il tuo compito, il che significa naturalmente che non ci sarà un minimo comune denominatore da usare (a meno che tu non lo faccia tu stesso).

Quindi, in linea di principio, è perfettamente possibile. Infatti, WINE esegue eseguibili di Windows direttamente su Linux.
Ma è un sacco di lavoro e (di solito) commercialmente ingiustificato.

* Nota: c'è molto di più in un file eseguibile oltre al semplice codice binario. C'è un sacco di informazioni che dice al sistema operativo che cosa librerie il file dipende, la quantità di memoria di stack di cui ha bisogno, le funzioni che esporta in altre librerie che possono dipendere da esso, in cui il sistema operativo potrebbe trovare le informazioni di debug rilevanti, come " riposizionare "il file in memoria, se necessario, come far funzionare correttamente la gestione delle eccezioni, ecc. ecc. di nuovo, potrebbe esserci un unico formato per questo su cui tutti sono d'accordo, ma semplicemente non lo è.


Curiosità: esiste un formato binario posiz standardizzato, che può essere eseguito su tutti i sistemi operativi. Non è solo comunemente usato.
Marcin,

@Marcin: Sembra che tu non consideri Windows come un sistema operativo. (O stai dicendo che Windows può eseguire i binari POSIX ?!) Ai fini della mia risposta POSIX non è il tipo di standard a cui mi riferisco. La X in POSIX sta per Unix. Non è mai stato progettato per essere utilizzato ad esempio da Windows, anche se Windows ha un sottosistema POSIX.
Mehrdad,

1. Qualcosa può essere eseguito su più sistemi operativi senza correre su tutti i sistemi operativi; 2. Windows da NT è stato in grado di eseguire binari posix.
Marcin,

1
@Marcin: (1) Come ho detto, la X in POSIX sta per UNIX . Non è uno standard che avrebbe dovuto essere seguito da altri sistemi operativi, era solo un tentativo di raggiungere un denominatore comune tra i vari Unix, il che è fantastico, ma non così sorprendente. Il fatto che ci siano più versioni di sistemi operativi Unix là fuori è completamente irrilevante al punto che ho cercato di fare riguardo alla compatibilità con altri sistemi operativi diversi da Unix. (2) Potete fornire un riferimento per # 2?
Mehrdad,

1
@Mehrdad: Marcin ha ragione; Windows SUA (sottosistema per applicazioni Unix) è conforme POSIX
MSalters,

5

Il diagramma ha il livello "applicazione" (principalmente) separato dal livello "sistema operativo" dalle "librerie", e ciò implica che "applicazione" e "sistema operativo" non hanno bisogno di conoscersi. Questa è una semplificazione nel diagramma, ma non è del tutto vera.

Il problema è che la "libreria" ha in realtà tre parti: l'implementazione, l'interfaccia per l'applicazione e l'interfaccia per il sistema operativo. In linea di principio, i primi due possono essere resi "universali" per quanto riguarda il sistema operativo (dipende da dove lo si suddivide), ma la terza parte - l'interfaccia per il sistema operativo - generalmente non può. L'interfaccia con il sistema operativo dipenderà necessariamente dal sistema operativo, dalle API fornite, dal meccanismo di impacchettamento (ad esempio il formato di file utilizzato dalla DLL di Windows), ecc.

Poiché la "libreria" viene generalmente resa disponibile come un singolo pacchetto, significa che una volta che il programma seleziona una "libreria" da utilizzare, si impegna in un sistema operativo specifico. Ciò può avvenire in due modi: a) il programmatore seleziona completamente in anticipo e quindi l'associazione tra la libreria e l'applicazione può essere universale, ma la libreria stessa è vincolata al sistema operativo; oppure b) il programmatore imposta le cose in modo che la libreria venga selezionata quando si esegue il programma, ma il meccanismo di associazione stesso, tra il programma e la libreria, dipende dal sistema operativo (ad esempio, il meccanismo DLL in Windows). Ognuno ha i suoi vantaggi e svantaggi, ma in entrambi i casi devi fare una scelta in anticipo.

Ora, questo non significa che sia impossibile da fare, ma devi essere molto intelligente. Per superare il problema, dovresti seguire la strada del prelievo della libreria in fase di esecuzione e dovresti trovare un meccanismo di associazione universale che non dipende dal sistema operativo (quindi sei responsabile di mantenerlo, molto più lavoro). Alcune volte ne vale la pena.

Non è necessario, ma se hai intenzione di sforzarti a farlo, ci sono buone probabilità che tu non voglia essere legato a un processore specifico, quindi scriverai una Macchina Virtuale e compilerai il programma in un formato di codice neutro del processore.

Ormai avresti dovuto notare dove sto andando. Le piattaforme linguistiche come Java fanno esattamente questo. Il runtime Java (libreria) definisce l'associazione neutrale del sistema operativo tra il programma Java e la libreria (come il runtime Java si apre ed esegue il programma) e fornisce un'implementazione specifica per il sistema operativo corrente. .NET fa la stessa cosa in una certa misura, tranne per il fatto che Microsoft non fornisce una "libreria" (runtime) per tutto tranne Windows (ma altri lo fanno - vedi Mono). E, in realtà, anche Flash fa la stessa cosa, sebbene abbia una portata più limitata per il browser.

Infine, ci sono modi per fare la stessa cosa senza un meccanismo di associazione personalizzato. È possibile utilizzare strumenti convenzionali, ma rinviare il passo di associazione alla libreria fino a quando l'utente sceglie il sistema operativo. Questo è esattamente ciò che accade quando si distribuisce il codice sorgente. L'utente prende il tuo programma e lo lega al processore (compilalo) e al sistema operativo (collegalo) quando l'utente è pronto per eseguirlo.

Tutto dipende da come si tagliano i livelli. Alla fine della giornata, hai sempre un dispositivo di elaborazione realizzato con hardware specifico che esegue codice macchina specifico. Gli strati sono in gran parte come un quadro concettuale.


3

Il software non è sempre specifico del sistema operativo. Sia Java che il precedente sistema p-code (e persino ScummVM) consentono software portatile su tutti i sistemi operativi. Anche Infocom (i produttori di Zork e Z-machine ) disponeva di un database relazionale basato su un'altra macchina virtuale. Tuttavia, a un certo livello qualcosa deve tradurre anche quelle astrazioni in istruzioni reali da eseguire su un computer.


3
Java viene eseguito su una macchina virtuale, che non è cross-OS. Devi usare un binario JVM diverso per ogni SO
Izkata

3
@Izkata True, ma non ricompilare il software (solo JVM). Vedi anche la mia ultima frase. Ma sottolineerò che Sun aveva un microprocessore che poteva eseguire direttamente il codice byte.
Elliott Frisch,

3
Java è un sistema operativo, sebbene di solito non sia pensato come tale. Il software Java è specifico del sistema operativo Java e ci sono emulatori del sistema operativo Java per la maggior parte dei sistemi operativi "reali". Ma potresti fare la stessa cosa con qualsiasi host e sistema operativo di destinazione, ad esempio eseguendo il software Windows su Linux usando WINE.
user253751

@immibis Sarei più specifico. Java Foundation Classes (JFC, la libreria standard di Java) è un framework. Java stesso è un linguaggio. La JVM è simile a un sistema operativo: ha "Virtual Machine" nel suo nome ed esegue funzioni simili a un sistema operativo dal punto di vista del codice in esecuzione in esso.

1

Tu dici

i software prodotti utilizzando linguaggi di programmazione per determinati sistemi operativi funzionano solo con essi

Ma il programma che fornisci come esempio funzionerà su molti sistemi operativi e persino su alcuni ambienti bare metal.

La cosa importante qui è la distinzione tra il codice sorgente e il binario compilato. Il linguaggio di programmazione C è specificamente progettato per essere indipendente dal SO nella forma sorgente. Lo fa lasciando l'interpretazione di cose come "stampa sulla console" fino all'implementatore. Ma C può essere rispettato a qualcosa che è specifico del sistema operativo (vedi altre risposte per motivi). Ad esempio, i formati eseguibili PE o ELF.


6
Sembra abbastanza chiaro che l'OP sta chiedendo dei binari, non del codice sorgente.
Caleb,

0

Altre persone hanno coperto bene i dettagli tecnici, vorrei menzionare un motivo meno tecnico, il lato UX / UI delle cose:

Scrivi una volta, sentiti a disagio ovunque

Ogni sistema operativo ha le proprie API di interfaccia utente e standard di progettazione. È possibile scrivere un'interfaccia utente per un programma e farlo funzionare su più sistemi operativi, comunque facendo tutto ma garantisce che il programma si sentirà fuori posto ovunque. La creazione di una buona interfaccia utente richiede l'ottimizzazione dei dettagli per ciascuna piattaforma supportata.

Molti di questi sono piccoli dettagli, ma sbagliali e frustrerai i tuoi utenti:

  • Conferma che le finestre di dialogo hanno i loro pulsanti in ordine diverso in Windows e OSX; sbagliare e gli utenti faranno clic sul pulsante sbagliato dalla memoria muscolare. Windows ha "Ok", "Annulla" in questo ordine. OSX ha cambiato l'ordine e il testo del pulsante fai-da-te è una breve descrizione dell'azione da eseguire: "Annulla", "Sposta nel cestino".
  • Il comportamento "Torna indietro" è diverso per iOS e Android. Le applicazioni iOS disegnano il proprio pulsante Indietro, se necessario, in genere in alto a sinistra. Android ha un pulsante dedicato in basso a sinistra o in basso a destra a seconda della rotazione dello schermo. Le porte rapide per Android si comporteranno in modo errato se il pulsante Indietro del sistema operativo viene ignorato.
  • Lo scorrimento del momento è diverso tra iOS, OSX e Android. Purtroppo, se non stai scrivendo un codice UI nativo, probabilmente dovrai scrivere il tuo comportamento di scorrimento.

Anche quando è tecnicamente possibile scrivere una base di codice UI che funziona ovunque, è meglio apportare modifiche per ciascun sistema operativo supportato.


-2

Una distinzione importante a questo punto è quella di separare il compilatore dal linker. Molto probabilmente il compilatore produce più o meno lo stesso output (le differenze sono dovute principalmente a varie #if WINDOWSs). Il linker, d'altra parte, deve gestire tutte le cose specifiche della piattaforma - collegare le librerie, costruire il file eseguibile ecc.

In altre parole, il compilatore si preoccupa principalmente dell'architettura della CPU, perché sta producendo il codice eseguibile effettivo e deve utilizzare le istruzioni e le risorse della CPU (si noti che il codice IL o JVM di .NET sarebbe considerato un set di istruzioni di una CPU virtuale in questa vista). Questo è il motivo per cui è necessario compilare il codice separatamente per x86e ARM, ad esempio.

Il linker, d'altra parte, deve prendere tutti questi dati e istruzioni grezzi e metterli in un formato che il caricatore (in questi giorni, questo sarebbe quasi sempre il sistema operativo) può capire, oltre a collegare qualsiasi libreria staticamente collegata (che include anche il codice richiesto per il collegamento dinamico, l'allocazione della memoria ecc.).

In altre parole, potresti essere in grado di compilare il codice una sola volta e farlo funzionare sia su Linux che su Windows, ma devi collegarlo due volte, producendo due diversi eseguibili. Ora, in pratica, spesso devi anche tenere conto del codice (è qui che entrano in gioco le direttive del (pre) compilatore), quindi anche compilare una volta il collegamento due volte non è molto usato. Per non parlare del fatto che le persone trattano la compilazione e il collegamento come un singolo passaggio durante la compilazione (proprio come a te non interessano più le parti del compilatore stesso).

Il software dell'era DOS era spesso più portabile binario, ma devi capire che era stato compilato non contro DOS o Unix, ma piuttosto contro un certo contratto che era comune alla maggior parte dei PC in stile IBM - scaricando ciò che oggi sono chiamate API software si interrompe. Questo non aveva bisogno di collegamenti statici, dato che dovevi solo impostare i registri necessari, chiamare ad es. int 13hPer le funzioni grafiche e la CPU è passata a un puntatore di memoria dichiarato nella tabella degli interrupt. Naturalmente, ancora una volta, la pratica era molto più complicata, perché per ottenere prestazioni da pedale al metal, dovevi scrivere tutti quei metodi da solo, ma ciò equivaleva sostanzialmente a aggirare il sistema operativo del tutto. E, naturalmente, c'è qualcosa che ha inevitabilmente bisogno dell'interazione con l'API del sistema operativo: la chiusura del programma. Tuttavia, se hai utilizzato i formati più semplici disponibili (ad esCOMsu DOS, che non ha intestazione, solo istruzioni) e non voleva uscire, beh, sei fortunato! E, naturalmente, potresti gestire anche la corretta terminazione nel runtime, quindi potresti avere sia il codice per la terminazione Unix che la terminazione DOS nello stesso eseguibile e rilevare in fase di esecuzione quale utilizzare :)


questo sembra ripetere semplicemente i punti spiegati in questo e questo precedenti risposte che sono state pubblicate ieri
moscerino
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.