PDA

View Full Version : [C] Comunicazione tra padre e figlio


Manugal
08-12-2006, 17:34
Ciao.

Devo scrivere un programma che legge dal proprio standard input quello che il figlio ha scritto sullo standard output. Il codice che ho scritto io è il seguente:


#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(){

pid_t pid;
int len;
ssize_t nread;
ssize_t nwrite;
int status;

if( (pid = fork()) < 0)
perror("fork failed");
else if( pid == 0){
char *stringa="Sono il figlio e ho scritto qualcosa";
len=strlen(stringa);
if( (nwrite = write(STDOUT_FILENO,stringa,len)) != len){
if(stringa != NULL){
free(stringa);
stringa=NULL;
}
perror("write error");
}
if(stringa != NULL){
free(stringa);
stringa=NULL;
}
exit(0);
}
else{
if ( wait(&status) != pid )
perror("waitpid error");
char *buf=(char *)malloc(sizeof(char)*100);
if( (nread = read(STDIN_FILENO,buf,100)) <= 0)
perror("read failed");
printf("Adesso dovrei scrivere quello che ha scritto il figlio: ");
printf("%s", buf);
if(buf != NULL){
free(buf);
buf=NULL;
}
}
printf("Ho finito!!!\n");
exit(0);
}


Il problema è che una volta che la stringa è stata scritta dal figlio, il programma si ferma (praticamente si ferma alla read del padre) come se fosse in attesa che io gli scrivessi qualcosa. Infatti qualsiasi stringa gli scrivo lui mi stampa quella (e non quella che aveva stampato il figlio su STDOUT). Perché? Spero di essere stato chiaro.

Grazie :)

mad_hhatter
08-12-2006, 19:11
Ciao.

Devo scrivere un programma che legge dal proprio standard input quello che il figlio ha scritto sullo standard output. Il codice che ho scritto io è il seguente:


#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(){

pid_t pid;
int len;
ssize_t nread;
ssize_t nwrite;
int status;

if( (pid = fork()) < 0)
perror("fork failed");
else if( pid == 0){
char *stringa="Sono il figlio e ho scritto qualcosa";
len=strlen(stringa);
if( (nwrite = write(STDOUT_FILENO,stringa,len)) != len){
if(stringa != NULL){
free(stringa);
stringa=NULL;
}
perror("write error");
}
if(stringa != NULL){
free(stringa);
stringa=NULL;
}
exit(0);
}
else{
if ( wait(&status) != pid )
perror("waitpid error");
char *buf=(char *)malloc(sizeof(char)*100);
if( (nread = read(STDIN_FILENO,buf,100)) <= 0)
perror("read failed");
printf("Adesso dovrei scrivere quello che ha scritto il figlio: ");
printf("%s", buf);
if(buf != NULL){
free(buf);
buf=NULL;
}
}
printf("Ho finito!!!\n");
exit(0);
}


Il problema è che una volta che la stringa è stata scritta dal figlio, il programma si ferma (praticamente si ferma alla read del padre) come se fosse in attesa che io gli scrivessi qualcosa. Infatti qualsiasi stringa gli scrivo lui mi stampa quella (e non quella che aveva stampato il figlio su STDOUT). Perché? Spero di essere stato chiaro.

Grazie :)

così su due piedi: la read del padre legge STDIN, mentre dovrebbe leggere STDOUT... no?

ora, se questo non fosse possbile (non ricordo) dovresti usare una pipe

Manugal
08-12-2006, 19:18
Ho provato a leggere STDOUT ma il risultato è lo stesso. A questo punto del programma non sono ancora arrivato alle pipe, quindi se un esercizio del genere mi viene messo ora che ancora non ho fatto le pipe, sicuramente c'è un metodo per risolverlo senza l'uso delle pipe.

mad_hhatter
08-12-2006, 19:31
Ho provato a leggere STDOUT ma il risultato è lo stesso. A questo punto del programma non sono ancora arrivato alle pipe, quindi se un esercizio del genere mi viene messo ora che ancora non ho fatto le pipe, sicuramente c'è un metodo per risolverlo senza l'uso delle pipe.

ma se legge STDOUT non può prendere l'input dalla tastiera :confused:

quando fai un fork, il figlio eredita lo stdin e stdout dal padre... forse vanno risettati opportunamente, in modo da risultare scambiati

Manugal
08-12-2006, 19:45
Ma scusa me l'hai detto tu che la read del padre dovrebbe leggere da STDOUT :D :confused: :confused:

così su due piedi: la read del padre legge STDIN, mentre dovrebbe leggere STDOUT... no?

E' normale infatti che lo faccio leggere da STDIN però non sortisce alcun effetto.

mad_hhatter
08-12-2006, 19:49
Ma scusa me l'hai detto tu che la read del padre dovrebbe leggere da STDOUT :D :confused: :confused:



E' normale infatti che lo faccio leggere da STDIN però non sortisce alcun effetto.


tu hai detto che leggendo stdout sicomporta allo stesso modo che leggendo stdin... questo non capivo, ora deduco (come è ovvio) che leggendo stdout NON si comporta ESATTAMENTE come quando legge stdin :)

ma appunto, se gli fai leggere lo stdin non beccherà mai l'output del figlio... se invece nel figlio riesci a invertire i file stdin e stdout sei a posto

Manugal
08-12-2006, 19:52
In che senso invertirli?

mad_hhatter
08-12-2006, 20:00
nel senso che per il processo figlio lo stdout dovrebbe essere rimappato sullo stdin del padre.

quando fai il fork il figlio eredita dal padre lo stdin e lo stdout... se ci fosse un modo per dire al figlio che quello che lui chiama stdout in realtà è lo stdout saremmo aposto... ma leggendo due righe in rete mi par di capire che solo il sistema operativo può scambiare gli IOstream... non so, magari sipuò fare, ma non so come

Manugal
08-12-2006, 20:50
Nessuno? :(

Qu@ker
08-12-2006, 22:23
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main()
{
int fd[2];
pid_t pid;

if (pipe(fd) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid < 0) {
perror("fork()");
exit(EXIT_FAILURE);
}
if (pid == 0) {
char *stringa = "Sono il figlio e ho scritto qualcosa";
int len = strlen(stringa);

close(fd[0]); //non leggo
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
if (write(STDOUT_FILENO, stringa, len) < 0) {
perror("write()");
}
_exit(0);
} else {
int status;
char *buf = malloc(100);
if (! buf) {
perror("malloc()");
exit(EXIT_FAILURE);
}

close(fd[1]); //non scrivo
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
if (read(STDIN_FILENO, buf, 100) < 0)
perror("read()");
printf("Messaggio dal figlio: \'%s\'\n", buf);
free(buf);

if (wait(&status) != pid)
perror("waitpid()");
}
puts("Ho finito!!!");
return 0;
}

mad_hhatter
08-12-2006, 22:36
scusa, non conosco benissimo il c... hai usato una pipe? se sì, Manugal diceva che non può usarle perchè, credo, al corso che frequenta non gliele hanno introdotte... altre soluzioni?

Manugal
09-12-2006, 10:25
Devo ancora provarlo per vedere se funziona, però ho trovato una soluzione. Praticamente usando la dup2() faccio una copia STDOUT_FILENO su un nuovo file descriptor in modo da riuscire a leggere con il padre quello che ha scritto il figlio. Ecco il codice:


#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

int main(){

pid_t pid;
int len;
ssize_t nread;
ssize_t nwrite;
int status;
int fd;

if ( (fd = open("temp", O_WRONLY | O_CREAT | O_TRUNC)) == -1)
perror("open failed");

if( (pid = fork()) < 0)
perror("fork failed");
else if( pid == 0){
char *stringa="Sono il figlio e ho scritto qualcosa";
len=strlen(stringa);
if( (nwrite = write(STDOUT_FILENO,stringa,len)) != len){
if(stringa != NULL){
free(stringa);
stringa=NULL;
}
dup2(fd,STDOUT_FILENO);
close(fd);
perror("write error");
}
if(stringa != NULL){
free(stringa);
stringa=NULL;
}
lseek(fd,0,SEEK_SET);
exit(0);
}
else{
if ( wait(&status) != pid )
perror("waitpid error");
char *buf=(char *)malloc(sizeof(char)*100);
if( (nread = read(fd,buf,100)) <= 0)
perror("read failed");
printf("Adesso dovrei scrivere quello che ha scritto il figlio: ");
printf("%s", buf);
if(buf != NULL){
free(buf);
buf=NULL;
}
close(fd);
}
printf("Ho finito!!!\n");
exit(0);
}

Manugal
09-12-2006, 10:27
Ah neanche l'avevo letta la soluzione sopra :)

In pratica non so se funziona senza pipe.

mad_hhatter
09-12-2006, 14:51
Ah neanche l'avevo letta la soluzione sopra :)

In pratica non so se funziona senza pipe.

quindi in ogni caso usi un metodo di comunicazione tra processi, non ti affidi ai soli stdin e stdout, giusto?

Manugal
10-12-2006, 11:31
Si in un certo senso si è comunicazione tra processi. Però l'uso delle pipe ancora non l'ho introdotto. Ora lo proverò e vedrò se funziona. ;)

Manugal
10-12-2006, 13:01
Niente da fare anche con quel codice scritto da me poco fa, il programma ha lo stesso comportamento cioè attende che io gli scriva qualcosa (invece di leggere quello che ha scritto il figlio). :cry:

ilsensine
11-12-2006, 11:14
Niente da fare anche con quel codice scritto da me poco fa, il programma ha lo stesso comportamento cioè attende che io gli scriva qualcosa (invece di leggere quello che ha scritto il figlio). :cry:
La dup2 la hai inserita un pò "a casaccio", rileggiti il codice copo un caffè ;)

Un pipe è la soluzione corretta. Anche usando dup2 correttamente non puoi avere l'effetto desiderato, per come sono fatti stdin/out.

L'utilizzo di un file temporaneo potrebbe funzionare, ammesso che prima correggi tutti i bug che hai introdotto :)

mad_hhatter
11-12-2006, 14:48
ecco, speravo proprio in un intervento di ilsensine... mi stavo giusto chiedendo come funzionano stdin/out in linux... hai voglia di illuminarmi perfavore?

grazie mille!

ilsensine
11-12-2006, 15:58
ls -l /proc/self/fd

Sono in genere connessi a uno (pseudo)terminale. Quello che ci scrivi, viene letto da chi gestisce il terminale (kernel o programma - tipo console per X - che sia).

mad_hhatter
11-12-2006, 22:47
ls -l /proc/self/fd

Sono in genere connessi a uno (pseudo)terminale. Quello che ci scrivi, viene letto da chi gestisce il terminale (kernel o programma - tipo console per X - che sia).

ma tecnicamente non sono file fisici, bensì cosa? buffer? e se il lettore li svuota ecco che anche se possediamo una copia del loro desrittore non ce ne facciamo nulla... è giusta come ipotesi? se lo è, immagino che sia anche il motivo per cui è indispensabile una pipe perchè essa invia immediatamente i dati al destinatario che quindi li riceve con certezza... è corretto o sto sbagliando?

grazie mille per le info!

ilsensine
12-12-2006, 09:04
Per come sono fatti i sistemi unix, sono semplicemente "file descriptor". Non c'è molta differenza se un descriptor è aperto su un char device (un terminale, ad es) o un file ( ls > file.txt). Dal punto di vista del programma, cambia ben poco: ovviamente però alcuni fd supportano alcune cose (ad es. i terminali supportano molte delle ioctl di termios), mentre un file supporterà altre cose (ad es. la mmap).

Prova questo programma ad esempio:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>

int main()
{
void *addr = mmap(NULL, getpagesize(), PROT_READ,
MAP_SHARED, fileno(stdin), 0);
if (addr==MAP_FAILED) {
perror("mmap");
} else {
fprintf(stderr, "stdin mmapped!\n");
munmap(addr, getpagesize());
}
return 0;
}

Eseguilo sia come
./programma
sia come
touch testfile.txt; ./programma < testfile.txt
e confronta i risultati :)

Puoi sapere cosa è effettivamente connesso a un file descriptor come stdin|out|err tramite la funzione fstat. La usano ad esempio alcuni programmi che chiedono la password, per essere sicuri che stdin è effettivamente un terminale.

Infine, giusto per generare un pò di ulteriore confusione :D, prova questo (assolutamente non portabile) programma:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
char buf[4096];
int sz, pos;

dprintf(fileno(stdout), "Ho scritto su stdout.\n");
dprintf(fileno(stderr), "Ho scritto su stderr.\n");
dprintf(fileno(stdin), "Ho scritto...su stdin!!!\n");

dprintf(fileno(stdout), "\nOra scrivi qualcosa e premi <invio>.\n");
sz = read(fileno(stdin), buf, sizeof(buf));
(sz>0) && !(buf[sz-1] = '\0') &&
dprintf(fileno(stdout), "Ho letto '%s' da stdin.\n", buf);

dprintf(fileno(stdout), "Ora scrivi qualcos'altro e premi <invio>.\n");
sz = read(fileno(stderr), buf, sizeof(buf));
(sz>0) && !(buf[sz-1] = '\0') &&
dprintf(fileno(stdout), "Ho letto '%s' da stderr!!!\n\n", buf);

for (pos=0; pos<110; ++pos)
dprintf(pos%3, "%c", "yeeeeep!!!\n"[pos%11]);

return 0;
}

Manugal
12-12-2006, 12:28
La dup2 l'ho provata in un po' tutte le posizione :) ma niente da fare. Credo proprio che l'unica soluzione sia la pipe. Non riesco proprio a capire come il prof abbia potuto mettermi un esercizio del genere senza avermi parlato di pipe. :confused:

Grazie a tutti cmq dell'aiuto.

mad_hhatter
14-12-2006, 11:47
Per come sono fatti i sistemi unix, sono semplicemente "file descriptor". Non c'è molta differenza se un descriptor è aperto su un char device (un terminale, ad es) o un file ( ls > file.txt). Dal punto di vista del programma, cambia ben poco: ovviamente però alcuni fd supportano alcune cose (ad es. i terminali supportano molte delle ioctl di termios), mentre un file supporterà altre cose (ad es. la mmap).

Prova questo programma ad esempio:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>

int main()
{
void *addr = mmap(NULL, getpagesize(), PROT_READ,
MAP_SHARED, fileno(stdin), 0);
if (addr==MAP_FAILED) {
perror("mmap");
} else {
fprintf(stderr, "stdin mmapped!\n");
munmap(addr, getpagesize());
}
return 0;
}

Eseguilo sia come
./programma
sia come
touch testfile.txt; ./programma < testfile.txt
e confronta i risultati :)

Puoi sapere cosa è effettivamente connesso a un file descriptor come stdin|out|err tramite la funzione fstat. La usano ad esempio alcuni programmi che chiedono la password, per essere sicuri che stdin è effettivamente un terminale.

Infine, giusto per generare un pò di ulteriore confusione :D, prova questo (assolutamente non portabile) programma:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
char buf[4096];
int sz, pos;

dprintf(fileno(stdout), "Ho scritto su stdout.\n");
dprintf(fileno(stderr), "Ho scritto su stderr.\n");
dprintf(fileno(stdin), "Ho scritto...su stdin!!!\n");

dprintf(fileno(stdout), "\nOra scrivi qualcosa e premi <invio>.\n");
sz = read(fileno(stdin), buf, sizeof(buf));
(sz>0) && !(buf[sz-1] = '\0') &&
dprintf(fileno(stdout), "Ho letto '%s' da stdin.\n", buf);

dprintf(fileno(stdout), "Ora scrivi qualcos'altro e premi <invio>.\n");
sz = read(fileno(stderr), buf, sizeof(buf));
(sz>0) && !(buf[sz-1] = '\0') &&
dprintf(fileno(stdout), "Ho letto '%s' da stderr!!!\n\n", buf);

for (pos=0; pos<110; ++pos)
dprintf(pos%3, "%c", "yeeeeep!!!\n"[pos%11]);

return 0;
}



ciao ilsensine, ho provato i 2 programmi, grazie mille che il tuo tempo...

scusa se ci ho messo cosi tanto ma dovevo finire la tesi :)

senti, ma io il secondo non l'ho mica capito sai? ok che non conosco bene il c e non conosco le spec della funzione read, ma mi stai dicendo che hai rimappato al volo lo stdin sullo stderr??

ilsensine
14-12-2006, 13:05
ma mi stai dicendo che hai rimappato al volo lo stdin sullo stderr??
No, ho semplicemente "barato": so che stdin|out|err su linux sono normalmente un dup2 dello stesso descrittore, quindi li ho usati...indifferentemente :D

Ovviamente è sbagliato fare questa assunzione; i tre descrittori possono essere connessi a tre cose diverse (o avere attributi diversi: sola lettura per stdin, sola scrittura per stdout e stderr). Esegui questo ad esempio:

ls -l /proc/self/fd/ 0</dev/zero 1>out.txt 2>/dev/null; cat out.txt

trallallero
14-12-2006, 14:06
(sz>0) && !(buf[sz-1] = '\0') &&
dprintf(fileno(stdout), "Ho letto '%s' da stderr!!!\n\n", buf);

questo sistema booleano mi piace usarlo nelle shell ma non ho mai pensato di usarlo in C.
In effetti é leggibilissimo anche qui. Prendo nota :D

ilsensine
14-12-2006, 15:41
questo sistema booleano mi piace usarlo nelle shell ma non ho mai pensato di usarlo in C.
In effetti é leggibilissimo anche qui. Prendo nota :D
Ah bene; tu pensa che, essendo quel programma sbagliato già in partenza, credevo di completare l'opera con un pò di "coding horror" :D

trallallero
14-12-2006, 16:02
Ah bene; tu pensa che, essendo quel programma sbagliato già in partenza, credevo di completare l'opera con un pò di "coding horror" :D
:D no a me piace veramente. Avró il gusto dell'orrido :)

Si ho letto della portabilitá del dprintf e in effeti fa un pó ridere :asd:

ilsensine
14-12-2006, 16:11
Si ho letto della portabilitá del dprintf e in effeti fa un pó ridere :asd:
Come osi? La dprintf è PORTABILISSIMA, è standard GNU :asd:

ilsensine
14-12-2006, 16:14
:D no a me piace veramente. Avró il gusto dell'orrido :)

...e infatti l'hai scambiata per una espressione booleana, mentre il secondo argomento (buf[sz-1] = '\0') è in realtà una assegnazione, con un bel "!" prima per rendere sempre vera la condizione :D

trallallero
14-12-2006, 16:23
...e infatti l'hai scambiata per una espressione booleana, mentre il secondo argomento (buf[sz-1] = '\0') è in realtà una assegnazione, con un bel "!" prima per rendere sempre vera la condizione :D
no che c'entra ... mi piace il sistema (mettici ció che vuoi) && (mettici ció che vuoi) && ...

e poi tu puoi anche assegnare ma in C la puoi usare come condizione (infatti metti il ! davanti)

io uso cose del genere in bash (magari piú complesse ma il succo é questo):
test -z "$NAME" && NAME=LOCALE
ma non ho mai pensato di usarle in C
Tutto qua ;)

ilsensine
14-12-2006, 16:28
io uso cose del genere in bash
In bash è particolarmente comodo perché mi scordo sempre dove sono [ e ], per scrivere un if :D

trallallero
14-12-2006, 16:38
In bash è particolarmente comodo perché mi scordo sempre dove sono [ e ], per scrivere un if :D
se il moderatore continua l'OT mi ritengo autorizzato a proseguire :D

pensa che qui la shell usata é la sh :Puke:
devono riscrivere tutto sempre e comunque! mi son dovuto scaricare la bash ed installarla di nascosto sulla mia macchina e su le alfa. Per fortuna che si sta migrando a linux :D

ilsensine
14-12-2006, 16:45
se il moderatore continua l'OT mi ritengo autorizzato a proseguire :D

Il moderatore non è mai OT per definizione, è sempre il titolo del thread ad essere sbagliato :D

trallallero
14-12-2006, 16:52
Il moderatore non è mai OT per definizione, è sempre il titolo del thread ad essere sbagliato :D
"la prima legge del moderatore" :D