Ho condotto un esperimento di scrittura di software per analizzare i risultati di una esportazione delle mie conversazioni con ChatGPT. Ma ho deciso di aiutarmi nell’analisi con un Large Language Model che sicuramente sa fare molto bene questo lavoro e in più con un LLM in locale, per vedere come funziona.
Negli ultimi giorni ho avuto problemi intermittenti con un mouse Bluetooth Trust (modello 24059-02) connesso al mio HP con Ubuntu. Il mouse ha funzionato regolarmente per un paio di giorni, poi improvvisamente ha smesso di …
Ho installato la versione 24.3.1.347 di Oracle SQL Developer però ho una sorpresa: sembra non sia possibile raggruppare le connessioni in cartelle. Risposta: Eh sì — è una sorpresa che ha colpito parecchi utenti anche …
Quando si esegue uno script Bash ci sono due modalità principali da impiegare, che hanno conseguenze diverse sull’ambiente della shell. 1. Esecuzione diretta (./script.sh) Quindi può solo leggere le variabili di ambiente della shell madre …
Preso dalla curiosità di ripassare l’assembly (tanti anni fa studiai il Motorola 68000) ho scritto una guida ordinata che illustra passo per passo come scrivere un semplice programma Assembly in real-mode (16 bit), confezionarlo come …
Conosciuto anche come Videos, Totem è un riproduttore di film progettato per GNOME [3].
Dopo aver avanzato il mio OS alla versione di Ubuntu 22.04 ho notato un problema (nel senso: da un po’ non vedevo i video e facendone girare uno delle vacanze estive mi sono accorto del probema che solo poi ho trovato essere legato alla nuova release di Ubuntu).
Facendo doppio click su un video, si apre il programma ma, invece di avviarsi il video, appare una finestra di alert con scritto
Il file non è stato trovato
Ho trovato la descrizione del problema nel forum [1] assieme alla soluzione, meglio spiegata in [2].
In breve, con la versione 22.04 di Ubuntu è stata introdotta una incompatibilità con GStreamer, un framework multimediale per gestire media file [4]. Con Ubuntu 22.04 non è più necessario per cui si può eliminare ed elimare con esso il problema:
Un piccolo breviario con la spiegazione succinta dei principali concetti riguardante i sistemi operativi (operating systems, OS).
Sistemi Operativi
Sono i software fondamentali per poter utilizzare di una macchina di tipo Von Neumann (quella col processore e la memoria per dati/programmi). Consentono di usare il processore, la memoria, il network, l’I/O, di gestire l’allocazione in memoria dei programmi e dei processi e di regolare il loro accesso alle risorse e le interazioni tra di loro.
Come diceva la mia prima insegnante di programmazione, un computer senza sistema operativo è solo ferraglia (hardware, in inglese).
Kernel
Il kernel è il sottoinsieme di programmi del sistema operativo che sovraintende all’accesso delle risorse hardware (processore, RAM, memoria di massa, rete e I/O). I programmi “utente” quelli che servono ad eseguire le azioni della vita quotidiana – far girare un sito web, gestiore un foglio di calcolo, montare un video, mantenere un database – accedono alle risorse hardware solamente per mezzo del sistema operativo (e se lo saltano la circostanza è eccezionale e si fa solo in casi estremi).
Quali sono i principali compiti svolti dal kernel dei sistemi operativi?
Sintetizzando al massimo si possono individuare nel kernel le seguenti 2 attività:
Allocazione delle risorse (memoria e I/O)
Pianificazione e gestione dei processi (scheduling, ciclo di vita, segnalazioni tra processi, arbitraggio)
Allocazione delle risorse (RAM e I/O)
Spazio kernel e spazio utente
I computer moderni dividono la memoria in spazio del kernel e spazio utente. Lo spazio utente è il luogo in cui viene eseguito il software applicativo, mentre lo spazio del kernel è dedicato al lavoro dietro le quinte necessario per far funzionare un computer, come l’allocazione della memoria e la gestione dei processi. A causa di questa separazione tra spazio kernel e spazio utente, il lavoro svolto dal kernel non è in genere visibile all’utente.
Il sistema operativo è responsabile dell’occupazione/liberazione dello spazio di memoria per i processi. Mantiene le mappature dalla memoria virtuale a quella fisica (che sono archiviate nelle tabelle delle pagine). Decide anche quanta memoria allocare a ciascun processo e quando un processo deve essere rimosso dalla memoria.
L’accesso alla memoria RAM è un processo molto dispendioso anche in termini di tempo. Per eseguire operazioni di I/O in memoria infatti il microprocessore si avvale di un dispositivo hardware di controllo dedicato detto Memory Controller. Il MC gestisce l’individuazione delle locazioni di memoria attraverso l’indirizzamento e l’I/O dei dati da e verso il processore attraverso l’infrastruttura del bus dati (data bus). Nei processori di ultima generazione il memory controller è stato integrato nei microprocessori (infatti si chiama IMC – Integrated Memory Controller) ma l’infrastruttura fisica del bus (con i suoi clock) e la RAM rimangono pur sempre periferici e quindi introducono latenze.
Se un programma deve inizalizzare delle variabili non va a prendersi la RAM direttamente, ma se la fa allocare dal kernel. Ogni processo ha a disposizione una memoria virtuale visibile come heap (“mucchio”, area di memoria utilizzata per variabili globali) o come stack (“pila”, area di memoria utilizzata per chiamate a funzione, ricorsione e variabili locali): per ogni processo il sistema operativo gli presenta quello che a lui sembra essere l’intervallo di memoria completamente indirizzabile. Quindi su una macchina a 32 bit, ogni processo “pensa” di avere a sua disposizione 4 GB di memoria contigua.
In realtà, il sistema operativo, dietro le quinte, è impegnato a mappare le allocazioni di memoria virtuale su blocchi reali di memoria fisica. Quindi, ad esempio, un’allocazione di memoria virtuale di 400 byte viene mappata su 100 blocchi fisici da 4 byte. Quei blocchi fisici non devono essere contigui (e quasi mai lo sono – nulla impedisce che accada, ma su una macchina che esegue qualsiasi tipo di lavoro, è altamente improbabile) ma l’allocazione della memoria virtuale deve essere contigua.
Memoria Virtuale
La memoria virtuale è una tecnica che dà a un programma applicativo l’impressione di avere una memoria di lavoro RAM non frammentata, mentre in realtà può essere fisicamente frammentata e si può persino estendere nello spazio di archiviazione su disco. I sistemi che utilizzano questa tecnica semplificano la programmazione di applicazioni di grandi dimensioni e utilizzano la memoria fisica reale (ad es. RAM) in modo più efficiente rispetto a quelli senza memoria virtuale.
Attenzione che la “memoria virtuale” non è solo “l’utilizzo dello spazio su disco per estendere le dimensioni della memoria fisica”. L’estensione della memoria è una normale conseguenza dell’utilizzo di tecniche di memoria virtuale, che può essere gestita con le sovrapposizioni (overlay) o lo scambio completo di programmi e relativi dati (swap) su disco mentre questi sono inattivi.
Per “memoria virtuale” si intende più precisamente l’inganno che viene fatto ai programmi facendo credere loro di utilizzare grandi blocchi di indirizzi contigui.
malloc
Facciamo un esempio. Un programma C alloca dinamicamente una locazione di memoria. Cioè lo fa a runtime, non viene riservata una memoria iniziale dal compilatore (ad esempio come quando dichiaro una variabile int) ma lo fa in corsa per esempio per dichiarare un array di dimensione arbitraria che lo user si inventa al momento, mentre il programma sta già girando.
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Questo puntatore contiene l'indirizzo base del blocco creato
int* ptr;
int n, i;
printf("Scrivi il numero di elementi dell'array:");
scanf("%d",&n);
// Alloca dinamicamente la memoria usando malloc()
ptr = (int*)malloc(n * sizeof(int));
...
In questo esempio al momento della compilazione non sappiamo quanta memoria servirà al programma perché è l’utente che lo decide dopo averlo avviato.
L’istruzione malloc() riserverà al processo, che rappresenta la realizzazione fisica del programma C, un’area di memoria (virtuale) n volte la dimensione di una variabile di tipo intero. Per esempio se la variabile intera è rappresentata in 32 bit (4 byte) e n=100, il processo dovrà allocare 400 byte.
Dove sono realmente questi 400 byte?Il programma non lo sa (il programmatore ancora meno).
Il kernel gli assegnerà 400 byte che lui – il programma – vedrà come contigui, ma dietro le quinte il kernel sa esattamente nella RAM fisica dove si trovano questi 400 byte che possono anche essere sparpagliati. Per mantenere questa associazione il kernel usa una tecnica chiamata paginazione (paging) che ha il compito non soltanto di fornire una mappatura tra indirizzi virtuali e fisici ma anche di segregare i processi in aree di memoria stagne e di fornire i puntamenti ai blocchi di RAM non contigui in modo tale che la memoria virtuale risulti non frammentata.
Gestione dei processi
Un programma si può definire come una sequenza (“passiva”) di istruzioni scritte in un determinato linguaggio che ha lo scopo di risolvere un problema o, più in generale, di effettuare un’attività.
Processo
È l’effettiva esecuzione delle istruzioni che definiscono il programma. Un programma può essere suddiviso in più processi; per esempio se un programma apre più finestre, ogni finestra è un processo separato. I processi sono definiti e gestiti dal sistema operativo.
Un singolo processore del computer esegue una o più istruzioni alla volta (per ciclo di clock), una dopo l’altra. Per consentire agli utenti di eseguire più programmi contemporaneamente (ad esempio, in modo che il tempo del processore non venga sprecato in attesa di input da una risorsa), i sistemi informatici a processore singolo possono eseguire la divisione del tempo o time sharing. La condivisione del tempo consente ai processi di passare da uno stato di esecuzione ad uno stato di attesa per continuare ad essere eseguiti. Nella maggior parte dei casi ciò avviene molto rapidamente, fornendo l’illusione che diversi processi vengano eseguiti “contemporaneamente”. Questo è noto come concorrenza o multiprogrammazione.
Pianificazione e gestione dei processi.
Il sistema operativo mantiene separati i suoi processi e alloca le risorse di cui hanno bisogno in modo che abbiano meno probabilità di interferire tra loro e causare errori di sistema (ad esempio deadlock – la situazione in cui due processi aspettano a vicenda che l’altro acceda ad una locazione di memoria – o thrashing – situazione in cui il processo spende più tempo a paginare la memoria che ad eseguire le istruzioni). Il kernel può anche fornire meccanismi per la comunicazione tra processi per consentire ai processi di interagire in modi sicuri e prevedibili.
Arbitraggio dei processi
Quando il kernel decide che bisogna parcheggiare un processo (P1) e servirne un altro (P2) in base a un qualche criterio, avviene il salvataggio in una particolare area della RAM del set di registri e flag del processore, del program counter (il registro che contiene l’indirizzo della prossima istruzione del programma da caricare nel processore) e dello stack (che rappresenta la nidificazione delle chiamate a funzione) relativi al processo P1 e preleva dalla stessa area, in una locazione diversa destinata al processo P2, i diversi valori per lo stesso set di registri e dello stack per il processo P2 e li carica nel processore per poi avviarne l’esecuzione. Questa sequenza scrittura – lettura si chiama commutazione del contesto (context switching).
Sincronizzazione dei processi concorrenti.
Se ci sono processi che concorrono a scrivere in un stessa locazione di memoria (ad esempio due utenti che condividono un conto bancario ed eseguono operazioni allo stesso momento), il sistema operativo deve orchestrare le operazioni su questa locazione di memoria perché questa rappresenti in ogni istante il risultato che tutti si attendono.
Esempio
Alice e Bob aprono un conto bancario condiviso. Il loro saldo iniziale è di 0 €. Ognuno di loro deposita 100 €. Ci aspettiamo che il saldo finale debba essere di 200 €.
Ma consideriamo la seguente sequenza di operazioni:
1) Alice legge Saldo (legge 0 €),
2) Alice incrementa Saldo ma non lo salva ancora: Saldo + 100 €: a questo proposito si consideri che l’istruzione seguente s = s + 100 viene eseguita in più passaggi dal processore (eax, eay sono registri interni del processore – il processore esegue tutte le operazioni aritmetiche internamente perché così opera in modo enormemente più veloce – e s la locazione di memoria virtuale che contiene il Saldo)
mov eay, 0
mov eax, 100
add eax, eay
push s
3) Bob legge il Saldo (legge anche lui 0 €, poiché Alice non ha ancora scritto il nuovo Saldo, non è ancora arrivata a push s),
4) Bob incrementa il Saldo e neanche lui lo sovrascrive: Saldo + 100 €,
5) Alice scrive il Saldo (scrive 100 €),
6) Bob scrive Saldo (scrive anche lui 100 €).
Questa sequenza porta al saldo finale di 100 € (che non è corretto). Questo esempio illustra chiaramente l’importanza della sincronizzazione tra i processi di Alice e Bob. In particolare, la seguente sequenza (se applicata) produrrebbe risultati corretti:
Alice legge,
Alice incrementa,
Alice scrive,
Bob legge,
Bob incrementa,
Bob scrive .
Potrebbero esserci altre sequenze di letture e scritture che producono ugualmente risultati corretti, però l’importante è vedere come il sistema operativo esegua l’arbitraggio dei due processi affinché venga eseguito prima completamente il processo di Alice – che è partito prima – e poi quello di Bob.
Commutazione del contesto
Un altro dei compiti del kernel è fare in modo che ogni processo abbia accesso alla CPU e alle sue risorse per un certo periodo limitato di tempo. Questo è dovuto al principio che un processore serve un processo alla volta. Ultimamente – negli ultimi 20 anni – con l’avvento dei processori multicore abbiamo effettivamente la possibilità di eseguire più processi in contemporanea moltiplicando il numero di CPU. Attualmente per i processori commerciali siamo a 8 core per cui si possono eseguire 8 processi contemporaneamente. Ma il principio è sempre quello: una CPU (un core) esegue un solo processo alla volta. La tecnica del multitasking prevede un uso a divisione di tempo della singola CPU (time sharing).
C’è un concetto che per tanti anni mi è apparso misterioso, quello di espressione lambda o lambda expression in inglese – che poi mi è risultato analogo a quello di funzione anonima – da quando me l’hanno raccontato nel corso di Automi e Linguaggi Formali all’Università nell’ambito del corso di LISP moltissimi anni fa.
Sì, faccio pubblicamente ammenda di questa cosa, dovrei saperlo e non lo so.
Non è in ogni caso un concetto particolarmente complicato.
Espressione lambda
Una espressione lambda è una funzione senza nome (o anonima) in cui descrivo solo quali sono gli argomenti e i risultati della funzioni.
Una sintassi generale non legata ad alcun linguaggio di programmazine potrebbe essere:
argomenti -> operazioni
Per chi ha qualche confidenza con la matematica, una definizione di funzione di solito si scrive così:
\begin{gathered}
f: \mathbb{R} \rightarrow \mathbb{R} \\
x \mapsto 2x
\end{gathered}
Ecco: l’epressione nella seconda riga è esattamente la lambda expression.
Definizione di una espressione lambda in Python
Vediamo come si definiscono e si utilizzano le espressioni lambda in vari linguaggi di programmazione.
In Python le funzioni ordinarie si definiscono con la keyword def
def double(x):
return 2*x
Quindi c’è la parola chiave def, poi il nome della funzione, l’elenco degli argomenti – tra parentesi tonde – e infine il corpo della funzione, complicato quanto si vuole.
Invece le espressioni lambda si definiscono con la keyword lambda, non è necessario dare loro un nome e occupano una sola riga:
lambda x: 2*x
quindi in generale
lambda argomenti: espressione
Poi per poterle invocare un nome bisogna comunque darglielo, ad esempio per l’espressione lambda precedente:
double = lambda x: 2*x
y = double(5)
Funzioni anonime in PHP
Le funzioni anonime in PHP sono l’equivalente delle espressioni lambda per Python:
Ora, si potrebbe obiettare che comunque diamo sempre un nome a queste funzioni anonime, per cui: sono anonime o no?
L’uso più appropriato che da il nome di funzione anonima è la definzione di una callback, ossia una funzione che deve venire invocata quando si verifica un determinato evento.
Utilizzo in Javascript
In Javascript possiamo fare questo esempio: una funzione viene chamata quando si verifica un evento (un click, del mouse, la pressione su un link, la digitazione di un tasto della tastiera e così via). La funzione da invocare (che viene detta callback) è definita e invocata sul posto e quindi non ha alcuna necessità di avere un nome:
document.queryselector("#callback-btn")
.addEventListener("click", function() {
console.log("User has clicked on the button!");
});
In questo esempio la funzione di callback viene passata alla funzione addEventListener() che ha lo scopo di agganciare l’evento “click” ad una funzione che svolge un determinato compito. In questo caso specifico, quando questo click si verifica, il programma va a scrivere una riga di log. Non c’è alcuna necessità di invocarle altrove questa funzione per cui va bene anche non darle un nome, forma a cui si perviene con la sintassi function() (senza il nome tra la keyword e le parentesi come si fa di solito).
Da tempo sono perseguitato da un errore che si ripete (per quanto sospetto) ad ogni aggiornamento di php e che riguarda la libreria curl.
Sia l’invocazione da Apache che da client mi presentano un errore con questo tono:
$ php -r "curl_init();"
PHP Warning: PHP Startup: Unable to load dynamic library 'curl.so' (tried:
/usr/lib/php/20210902/curl.so (/usr/lib/php/20210902/curl.so: undefined symbol: curl_mime_addpart, version CURL_OPENSSL_4),
/usr/lib/php/20210902/curl.so.so (/usr/lib/php/20210902/curl.so.so: undefined symbol: curl_mime_addpart, version CURL_OPENSSL_4)
) in Unknown on line 0
PHP Fatal error: Uncaught Error: Call to undefined function curl_init() in Command line code:1
Stack trace:
\#0 {main}
thrown in Command line code on line 1
Un paio di volte ho pensato di aver risolvto il problema (vedi link in fondo al post) ma il problema è risaltato fuori al primo aggiornamento proposto da Canonical.
È stato stressante non trovare una soluzione in tutti questi anni perché non ne avevo il tempo. Solo che bisogna ad un certo punto venire ad un compromesso tra le cose da fare e lo stress che ci procura il nostro lavoro. Passato un certo segno, lo stress non è più tollerabile per cui le cose da fare passano assolutamente in seconda categoria di importanza.
Fondamentalmente l’articolo che mi trovavo sempre a leggere quando googlavo su questo argomento è quello riportato con Risorsa web [1] con la risposta illuminante dello sviluppatore finlandese:
Sempre nell’articolo in cui todeveni spiega cos’è successo trovo questo comando che mi conferma che c’è un problema nella libreria:
$ ldd /usr/lib/php/20210902/curl.so
/usr/lib/php/20210902/curl.so:
/usr/local/lib/libcurl.so.4:
no version information available (required by /usr/lib/php/20210902/curl.so)
linux-vdso.so.1 (0x00007fffe63f1000)
...
Il comando ldd (List Dynamic Dependencies) stampa gli oggetti condivisi (librerie condivise) richiesti da ciascun programma o oggetto condiviso specificato nella riga di comando. Un esempio del suo utilizzo e output è il seguente:
Tornando al file /usr/lib/php/20210902/curl.so, se cerco le sue dipendenze trovo
marcob@jsbach:/etc/php$ ldd /usr/lib/php/20210902/curl.so
/usr/lib/php/20210902/curl.so: /usr/local/lib/libcurl.so.4: no version information available (required by /usr/lib/php/20210902/curl.so)
linux-vdso.so.1 (0x00007fffe63f1000)
libcurl.so.4 => /usr/local/lib/libcurl.so.4 (0x00007f9a73200000)
...
Attenzione: trovo che il file nella libreria di PHP (/usr/lib/php/20210902/) dipende da un file posizionato nalla cartella /usr/local/lib/ e però è quest’ultimo ad avere qualche problema.
Provo a localizzare il file libcurl.so.4; ce n’è una marea:
$ locate libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.0/amd64/libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.5/amd64/libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.8/amd64/libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.9/amd64/libcurl.so.4
....
/usr/local/lib/libcurl.so.4 <-- questo è il file che vedo con ldd /usr/lib/php/20210902/curl.so
/usr/local/lib/libcurl.so.4.4.0
/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/18.08/1f7a5575c84c1df838ff07540cbc155e1eb78fee764860cbcbfab6d766328588/files/lib/x86_64-linux-gnu/libcurl.so.4
...
Però effettivamente il php punta a quello nella cartella /usr/local/lib/.
Attenzione: se cerco quali sono le librerie da da cui dipende il file in questa cartella non vedo nessun errore:
$ ldd /usr/local/lib/libcurl.so.4 | grep curl
$
ma non trovo neanche una dipendenza.
Ora però vediamo chi è davvero questo file so:
$ ls -l /usr/local/lib/libcurl.so.4
lrwxrwxrwx 1 root root 16 set 7 11:23 /usr/local/lib/libcurl.so.4 -> libcurl.so.4.4.0
Questo file non fa riferimento ad una libreria di sistema ma al file libcurl.so.4.4.0 che è stato buildato il 23/05/2017 e che ormai non è più allineato con quello ufficiale rilasciato da Canonical.
La soluzione arriva dalla Risorsa Web [4].
Dunque siamo alla soluzione, che comprende due operazioni
Mi è capitato un paio di volte di rimanere in braghe di tela col network: il browser mi dice che non sono connesso a internet, un ping generico all’host che voglio raggiungere mi dice questo:
$ ping www.google.com
ping: www.google.com: Nome o servizio sconosciuto
Sintomo 2: posso fare ping direttamente ad un nameserver pubblico come quello di Google:
marcob@jsbach:~$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=15.9 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=115 time=15.8 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=115 time=15.7 ms
^C
Sì, ok, non è detto che la porta ICMP di qualunque server sia aperta perché PING sia un comando che sempre ci può dire se un host è vivo oppure no. Però nel caso di Google va bene.
Sembra quindi che la mia interfaccia di rete sia attiva ma non sia in grado di raggiungere i DNS, perché l’interfaccia ha effettivamente un indirizzo IP:
Spegnere e riaccendere l’interfaccia non da il risultato sperato
$ sudo if down/up enp1s0
Nemmeno il riavvio del servizio di networking:
$ sudo service networking restart
L’unico comando che funziona (che permette di resuscitare il nome a dominio) è aprire la configurazione di rete (Impostazioni > Rete) e impostare il Metodo ipv4 su “Automatico (DHCP”) (era impostato su “Manuale”…) :
GUI gestione network
Non so la causa di questo malfunzionamento, può essere che l’attivazione di una delle VPN che uso sconfiguri l’interfaccia cancellando i record DNS.
In effetti in questa schermata gli IP address dei server DNS primario e secondario sono scritti a mano (per un altro malfunzionamento di una VPN che non mi configura in automatico questi dati). Forse l’origine del problema è proprio qui.
Official Ubuntu logo since 2022 March; used in the 22.04 release for the first time.
Ci sono vari modi in Ubuntu per cercare informazioni su un pacchetto, in generale sono mutuamente esclusivi (un pacchetto si gestisce con un gestore o con l’altro, mai con tutti quanti).
$ apt-cache show conky
Package: conky
Architecture: all
Version: 1.12.2-1
Priority: optional
Section: universe/utils
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Vincent Cheng <vcheng@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 28
Depends: conky-std | conky-cli | conky-all
Filename: pool/universe/c/conky/conky_1.12.2-1_all.deb
Size: 3244
MD5sum: 471cf8fb8bcd0d625eb765dcd769a6e4
SHA1: 858a207e872da62504526dad7121734e4dcbed7c
SHA256: 64ea4228325b775b1f714cc8a1b26ca2d871de6142b8de70a017b4addf9b4b2b
SHA512: 2540b39ce9a36185646b70feba7fe42f2d62633e4aa7f8c1f14bbc8aa9a36c2b1708c2bf7f4d245835e790ff4f59ebe04ee9bc7be85c09c44bc0eb67ec33d312
Homepage: https://github.com/brndnmtthws/conky
Description-it: monitor di sistema altamente configurabile (pacchetto di transizione)
Conky è un monitor di sistema che può visualizzare quasi tutto, sulla
finestra root del desktop o in una propria finestra. Conky ha molti
oggetti incorporati, oltre alla capacità di eseguire programmi o script
esterni (sia esternamente sia attraverso l'uso interno di Lua).
.
Questo è un pacchetto fittizio per facilitare la transizione al nuovo
schema di pacchettizzazione. Può essere rimosso senza problemi dopo
l'aggiornamento/l'installazione.
Description-md5: 6ae2b0f9855afb8d0a149407cf95bd93
oppure
$ aptitude show conky
Pacchetto: conky
Versione: 1.12.2-1
Stato: non installato
Priorità: opzionale
Sezione: universe/utils
Responsabile: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architettura: all
Dimensione pacchetto installato: 28,7 k
Dipende: conky-std | conky-cli | conky-all
Descrizione: monitor di sistema altamente configurabile (pacchetto di transizione)
Conky è un monitor di sistema che può visualizzare quasi tutto, sulla finestra root del desktop o in una propria finestra. Conky ha molti oggetti incorporati, oltre alla capacità di eseguire programmi o
script esterni (sia esternamente sia attraverso l'uso interno di Lua).
Questo è un pacchetto fittizio per facilitare la transizione al nuovo schema di pacchettizzazione. Può essere rimosso senza problemi dopo l'aggiornamento/l'installazione.
Homepage: https://github.com/brndnmtthws/conky
oppure
$ apt show conky
Package: conky
Version: 1.12.2-1
Priority: optional
Section: universe/utils
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Vincent Cheng <vcheng@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 28,7 kB
Depends: conky-std | conky-cli | conky-all
Homepage: https://github.com/brndnmtthws/conky
Download-Size: 3.244 B
APT-Sources: http://it.archive.ubuntu.com/ubuntu jammy/universe amd64 Packages
Description: monitor di sistema altamente configurabile (pacchetto di transizione)
Conky è un monitor di sistema che può visualizzare quasi tutto, sulla
finestra root del desktop o in una propria finestra. Conky ha molti
oggetti incorporati, oltre alla capacità di eseguire programmi o script
esterni (sia esternamente sia attraverso l'uso interno di Lua).
.
Questo è un pacchetto fittizio per facilitare la transizione al nuovo
schema di pacchettizzazione. Può essere rimosso senza problemi dopo
l'aggiornamento/l'installazione.
oppure ancora
$ snap info sweethome3d-homedesign
name: sweethome3d-homedesign
summary: An interior design application to draw house plans & arrange furniture
publisher: Jean-Baptiste Lallement (jibel)
store-url: https://snapcraft.io/sweethome3d-homedesign
contact: https://launchpad.net/~jibel
license: GPL-2.0
description: |
Sweet Home 3D is a free interior design application that helps you place
your furniture on a house 2D plan, with a 3D preview.
Available at http://www.sweethome3d.com/, this program is aimed at people
who want to design their interior quickly, whether they are moving or they
just want to redesign their existing home. Numerous visual guides help you
draw the plan of your home and layout furniture. You may draw the walls of
your rooms upon the image of an existing plan, and then, drag and drop
furniture onto the plan from a catalog organized by categories. Each change
in the 2D plan is simultaneously updated in the 3D view, to show you a
realistic rendering of your layout.
snap-id: EPpzG3puHCgp1HI89uqVPTmAuKgbLhV1
channels:
latest/stable: 6.6 2021-08-25 (15) 140MB -
latest/candidate: 6.6 2021-08-25 (15) 140MB -
latest/beta: 6.6 2021-08-25 (15) 140MB -
latest/edge: 6.6 2021-08-25 (15) 140MB -
Che differenza c’è?
Con apt si accede al parco applicazioni ufficialmente distribuite da Canonical.
Con snap so accede ad altre applicazioni non facenti parte della distribuzione Ubuntu che in genere si scaricano con tutte le dipendenze necessarie. A volte queste applicazioni potrebbero non integrarsi alla perfezione con Ubuntu.
dpgk è il gestore di pacchetti di Debian di più basso livello, e apt/aptitude sono client che utilizzano dpkg per installare, aggiornare o rimuovere pacchetti. Nel man di dpkg infatti si legge:
dpkg is a medium-level tool to install, build, remove and manage Debian packages.
The primary and more user-friendly front-end for dpkg
as a CLI (command-line interface) is apt(8) and
as a TUI (terminal user interface) is aptitude(8).
dpkg itself is controlled entirely via command line parameters, which consist of exactly one action and zero or more options. The action-parameter tells dpkg what to do and options control the behavior of the action in some way.
Dunque
apt è la command-line interface (il comando come espresso dall’esempio sopra) di dpgk e
aptitude è la terminal user interface di dpkg, una interfaccia molto oldish che mi ricorda Norton Commander
Una volta avviato un container, come faccio ad accedere alla sua shell?
Prima di tutto occorre elencare i container:
$ sudo docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42d1ab376b0d sent_crunch "/bin/sh -c \"/bin/ba…" 12 minutes ago Exited (0) 12 minutes ago mybeautifulproject-app-1
e18da64098a2 postgis/postgis "docker-entrypoint.s…" 23 hours ago Up 10 minutes (healthy) 0.0.0.0:5433->5432/tcp, :::5433->5432/tcp mybeautifulproject-postgres_db-1
Qui nel mio caso l’unico container running (al quale ha senso quindi collegarsi) è il e18da64098a2. Quindi
Lo sviluppo di applicazioni con l’utilizzo di Docker può diventare difficile quando si creano più container per erogare più servizi. Impariamo Docker Compose, lo strumento che ci aiuterà a eseguire ambienti applicativi multi-container.
In questo tutorial impareremo tutto su Docker Compose, i vantaggi dell’utilizzo di questo strumento, i suoi casi d’uso e le sue funzionalità.
Cos’è Docker Compose?
Un’applicazione può essere costituita da più contenitori che eseguono servizi diversi. Può essere complicato avviare e gestire i contenitori manualmente, quindi Docker ha creato uno strumento utile che aiuta ad accelerare il processo: Docker Compose.
Docker Compose è un software utilizzato per definire ed eseguire applicazioni Docker multi-container. Può gestire più container contemporaneamente nell’ambiente di produzione, staging, sviluppo, test e CI [Continuous Integration]. Pertanto, utilizzare Docker Compose per gestire l’intero ciclo di vita dello sviluppo del software [SDLC, software development life cycle].
Docker Compose funziona applicando le regole definite in un file docker-compose.yaml. Il file YAML configura i servizi dell’applicazione e include regole che specificano come si desidera che vengano eseguiti. Con questo file, puoi avviare, interrompere o ricostruire tutti i servizi utilizzando un unico comando. Inoltre, puoi controllare lo stato di un servizio, visualizzare gli output del registro ed eseguire comandi una tantum.
Docker Compose viene fornito in due “gusti”:
standalone: occorre installare un nuovo programma che è docker-compose, per questo di veda [2].
full package: se si è installato docker, sarà anche disponibile l’opzione compose per cui si usa il comando docker compose senza dover installare altro.
Casi d’uso di Docker Compose
I casi in cui risulta utile usare Docker Compose sono i seguenti:
Ambienti di test automatizzati. Compose supporta il test automatizzato, che è una parte essenziale di CI/CD in quanto può creare e distruggere facilmente l’ambiente di test richiesto. Gli sviluppatori possono definire e configurare l’ambiente necessario per l’esecuzione di test end-to-end automatizzati utilizzando il file Docker Compose appropriato.
Installazioni su singolo host. In Docker Compose, i container sono progettati per essere eseguiti su un singolo host poiché sono stati tradizionalmente focalizzati sullo sviluppo e sui flussi di lavoro di test.
Ambienti di sviluppo. Compose è un modo semplice e veloce per avviare progetti in quanto può creare rapidamente nuovi ambienti di sviluppo isolati. Il software documenta e configura tutte le dipendenze del servizio dell’applicazione (inclusi database, cache, API del servizio Web, ecc.). Consente di creare e avviare uno o più contenitori per ciascuna dipendenza utilizzando un unico comando.
I vantaggi di Docker Compose
Ecco alcuni dei principali vantaggi dell’utilizzo di Docker Compose:
Configurazione semplice e veloce. Grazie agli script YAML e alle variabili di ambiente, puoi configurare o modificare facilmente i servizi applicativi.
Comunicazione interna sicura. Compose crea una rete per tutti i servizi da condividere. Ciò aggiunge un ulteriore livello di sicurezza per l’app poiché non è possibile accedere ai servizi dall’esterno.
Portabilità e supporto CI/CD. Poiché tutti i servizi sono definiti all’interno del file docker-compose, gli sviluppatori possono facilmente accedere e condividere l’intera configurazione. Estraendo il file YAML e il codice sorgente, possono avviare l’ambiente in pochi minuti. Ciò contribuisce a creare e abilitare una pipeline CI/CD efficiente.
Uso efficiente delle risorse. Docker Compose consente di ospitare più ambienti isolati su un solo host. L’esecuzione di tutto su un singolo componente hardware ti consente di risparmiare molte risorse. Le sue caratteristiche che gli consentono di memorizzare una configurazione nella cache e di riutilizzare i contenitori esistenti, contribuiscono all’uso efficiente delle risorse.
Caratteristiche di Docker Compose
Esistono diverse importanti funzionalità di Docker Compose che offrono i vantaggi sopra menzionati.
Hosting di più ambienti isolati su un singolo host
Per impostazione predefinita, il nome del progetto è il nome di base della directory del progetto, mentre la directory del progetto è la directory di base del file docker-compose.yml. È possibile modificare i valori predefiniti:
Impostando il nome del progetto utilizzando l’opzione della riga di comando -p o la variabile di ambiente COMPOSE_PROJECT_NAME.
Impostando la directory del progetto usando la variabile di ambiente il parametro --project-directory da linea di comando.
È possibile utilizzare questa funzionalità su un host di sviluppo per eseguire copie stabili di ciascun ramo di funzionalità del progetto creando più copie dell’ambiente con nomi diversi.
Supporto delle variabili di ambiente
È possibile personalizzare i contenitori per ambienti o utenti diversi aggiungendo variabili di ambiente nel file docker-compose. Ciò offre maggiore flessibilità durante la configurazione dei contenitori con Compose, poiché i valori delle variabili non sono codificati nella configurazione.
I valori delle variabili possono essere impostati nell’ambiente della shell (da cui si esegue docker-compose) o in un file .env (memorizzato nella directory del progetto). Per impostazione predefinita, Docker Compose applica i valori specificati nel file .env. Tuttavia, i valori impostati nell’ambiente shell hanno la precedenza su quelli del file .env.
Ad esempio posso definire così il nome del progetto, in base a quanto visto poco fa: nella directory del progetto (quella in cui c’è il file docker-compose-yml) creo un file .env che contiene la linea:
COMPOSE_PROJECT_NAME=My Beautiful Project
Compose cerca il file automaticamente nella directory del progetto, oppure nelle variabili di ambiente impostati da shell.
Attenzione! I valori impostati nell’ambiente shell hanno la precedenza su quelli impostati nel file .env.
Se lancio la composizione del progetto, tra i log leggo:
$ sudo docker compose up
[+] Running 2/2
⠿ Container mybeautifulproject-postgres_db-1 Recreated 0.2s
⠿ Container mybeautifulproject-app-1 Created
.....
Conservazione dei dati sul volume
Un’altra grande caratteristica di Docker Compose è che salva i dati utilizzati dai servizi. Pertanto, non devi preoccuparti di perdere i dati creati nei contenitori. Se sono presenti contenitori di esecuzioni precedenti, Compose li troverà e copierà i relativi volumi nella nuova esecuzione.
Riutilizzo dei contenitori esistenti
Compose ricrea solo i contenitori che sono stati modificati dall’ultima esecuzione. Se non ci sono modifiche, riutilizza il contenitore esistente.
Questa funzione si basa sulla capacità del software di memorizzare nella cache le configurazioni dei container, consentendoti di configurare i tuoi servizi più velocemente.
I comandi di base di Docker Compose
L’uso di Compose è fondamentalmente un processo in tre fasi:
Definire l’ambiente della app con un Dockerfile in modo che possa essere riprodotto ovunque.
Definire i servizi che compongono la app nel file docker-compose.yml in modo che possano essere eseguiti insieme in un ambiente isolato.
Esegui docker compose up (docker <spazio> compose) e il comando Docker Compose si avvia ed esegue l’intera app. In alternativa, puoi eseguire docker-compose up (docker <trattino> compose) utilizzando Composer standalone (docker-compose eseguibile binario, attenzione al trattino!).
Un esenpio di file docker-compose.yml è il seguente:
Nell’applicazione che sto progettando e che farà utilizzo di Docker, ci sono anlcune directory host che voglio vengano viste dal container dell’applicaizone. Per fare questo in Docker si usa il concetto di volume.
C’è una directory che sarà destinata ad ospitare file prodotti dall’applicazione Python che andrà a costituire il mio primo volume. Per ora mi va bene che sia una directory isolata del mio host che monto nell’immagine che conterrà la mia applicazione dockerizzata. Più avanti mi ripropongo di montare la partizione di un NAS.
Il secondo volume che mi serve è quello che contiene il codice sorgente: voglio lavorare nell’host normalmente e fare in modo che il container Docker possa accedere al codice sorgente Python up to date. Voglio evitare la situazione in cui sviluppo nell’host e poi copio, ad ogni piccola modifica, i file dentro alla directory del contenitore docker.
Voglio in definitiva che il container veda il codice live.
Montaggio di una directory host come volume di dati Docker
Possiamo quindi montare una directory esistente dall’host a un container. Questi tipi di volumi sono chiamati Volumi Host.
È possibile montare i volumi host utilizzando il flag -v e specificando il nome della directory host.
Tutto ciò che si trova all’interno della directory host è quindi disponibile nel contenitore. Inoltre, tutti i dati generati all’interno del contenitore e inseriti nel volume di dati vengono archiviati in modo sicuro nella directory host e posso accedervi dall’OS dell’host con la shell o con Nautilus.
La sintassi di base per il montaggio di una directory host è:
$ docker run -v "$(pwd)":[volume_name] [docker_image]
L’attributo "$(pwd)" indica a Docker di montare la directory in cui si trova attualmente l’utente.
L’esempio seguente illustra come farlo.
1. Innanzitutto, crea una directory di esempio sull’host con il nome tmp e spostati in essa:
$ mkdir tmp && cd tmp
2. Una volta all’interno della directory, crea un file di test per vedere se sarà disponibile dal contenitore:
$ touch file.txt
3. Controllare se esiste l’immagine che vogliamo istanziare:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> e7a5d4ae5d10 3 days ago 1.44GB
sent_crunch latest 6dcb0bbb67c7 3 days ago 1.27GB
Nel mio caso l’immagine da usare è sent_crunch.
4. Quindi, usa il comando docker run per avviare un contenitore Ubuntu con la directory host collegata ad esso:
$ docker run -it -v "$(pwd)":/data1 [docker_image]
data1 è il nome del volume visto dal contenitore Docker. In definitiva, questo comando avvia il contenitore in modalità interattiva e monta un volume con il nome data1.
Nel mio esempio:
$ cd IdeaProjects/Python/SentCrunch
$ docker run -it -v "$(pwd)":/src sent_crunch
root@fbaaee970e3c:~# pwd
/app
root@fbaaee970e3c:~# ls
root@fbaaee970e3c:~# cd ..
root@fbaaee970e3c:/# ls
app bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin src srv sys tmp usr var
root@fbaaee970e3c:~# cd /src <----- ho montato qui la directory sorgente
root@fbaaee970e3c:/src# ls -l
total 260
-rw-rw-r-- 1 root root 1626 Aug 12 13:52 Dockerfile
-rwxrwxr-x 1 root root 10758 Feb 17 15:23 GPF_Smart_Agri-sat_Cruncher.xml
-rw-rw-r-- 1 root root 359 Dec 24 2021 Logger.py
-rw-rw-r-- 1 root root 2523 Oct 12 2021 Resample.xml
-rw-rw-r-- 1 root root 542 Nov 22 2021 SentinelCruncher.iml
-rw-rw-r-- 1 root root 104523 Jul 8 15:02 SentinelCruncher.py
-rw-rw-r-- 1 root root 0 Feb 1 2022 __init__.py
drwxrwxr-x 2 root root 4096 Jul 8 15:21 __pycache__
drwxrwxr-x 2 root root 4096 Jul 11 09:19 archexport
-rw-rw-r-- 1 root root 10396 May 10 09:50 changelog.txt
-rw-rw-r-- 1 root root 1536 May 9 09:22 constant.local.py
-rw-rw-r-- 1 root root 1563 Jul 8 15:14 constant.py
-rw-rw-r-- 1 root root 1926 May 9 09:18 constant.stage.py
-rw-rw-r-- 1 root root 665 Nov 9 2021 country.xml
-rw-rw-r-- 1 root root 1086 Aug 9 13:08 docker-compose.yml
-rw-rw-r-- 1 root root 851 Jul 27 06:14 docker_compose.yml
-rw-rw-r-- 1 root root 0 Jul 8 10:21 exportFile.dmp
-rw-rw-r-- 1 root root 16799 Oct 7 2021 install_pg.log
drwxrwxr-x 3 root root 4096 Nov 18 2021 latex
-rw-rw-r-- 1 root root 34498 Feb 25 16:02 libcruncher.py
-rw-rw-r-- 1 root root 0 Nov 18 2021 math
-rw-rw-r-- 1 root root 132 Jul 11 08:37 shp2psql.cmd
drwxrwxr-x 3 root root 4096 Jul 25 14:58 sql
drwxrwxr-x 2 root root 4096 Jul 11 16:34 test
-rw-rw-r-- 1 root root 10785 Jan 28 2022 xmlOut.xml
root@fbaaee970e3c:/src# cd test
root@fbaaee970e3c:/src/test# ./test001_dbconnect.py
Ciao Docker!
Utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Lo facciamo per migliorare l'esperienza di navigazione e per mostrare annunci personalizzati. Il consenso a queste tecnologie ci consentirà di elaborare dati quali il comportamento di navigazione o gli ID univoci su questo sito. Il mancato consenso o la revoca del consenso possono influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Commenti recenti