# [perl] Problème en invoquant sys/ioctl.ph (résolu)

## razer

Hello,

J'ai un problème, à priori nouveau, avec perl.

Je travaille sur un projet perl qui utilise sys/ioctl.ph pour manipuler un handler

Lors de l'exécution, le message suivant apparaît uniquement lorsque la ligne require "sys/ioctl.ph"; est présente dans mon programme :

```
Constant subroutine __USE_POSIX undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 8.

Constant subroutine __USE_POSIX2 undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 9.

Constant subroutine __USE_POSIX199309 undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 10.

Constant subroutine __USE_POSIX199506 undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 11.

Constant subroutine __USE_LARGEFILE undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 16.

Constant subroutine __USE_FILE_OFFSET64 undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 18.

Constant subroutine __USE_BSD undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 19.

Constant subroutine __USE_SVID undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 20.

Constant subroutine __USE_MISC undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 21.

Constant subroutine __GNU_LIBRARY__ undefined at /usr/lib/perl5/site_perl/5.8.8/i686-linux/features.ph line 156.

Operator or semicolon missing before &__inline at (eval 77) line 1.

Ambiguous use of & resolved as operator & at (eval 77) line 1.

Can't locate asm-generic/ioctl.ph in @INC (did you run h2ph?) (@INC contains: /home/razer/.gnome2/mailpictures/ /etc/perl /usr/lib/perl5/vendor_perl/5.8.8/i686-linux /usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl /usr/lib/perl5/site_perl/5.8.8/i686-linux /usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl/5.8.7 /usr/lib/perl5/site_perl/5.8.7/i686-linux /usr/lib/perl5/site_perl /usr/lib/perl5/5.8.8/i686-linux /usr/lib/perl5/5.8.8 /usr/local/lib/site_perl .) at /usr/lib/perl5/site_perl/5.8.8/i686-linux/asm/ioctl.ph line 5.

Compilation failed in require at /usr/lib/perl5/site_perl/5.8.8/i686-linux/asm/ioctls.ph line 7.

Compilation failed in require at /usr/lib/perl5/site_perl/5.8.8/i686-linux/bits/ioctls.ph line 8.

Compilation failed in require at /usr/lib/perl5/site_perl/5.8.8/i686-linux/sys/ioctl.ph line 8.

Compilation failed in require at ./mailpictures.pl line 10.

```

Je peux rendre l'erreur non fatale en faisant un lien symbolique de /usr/lib/perl5/site_perl/5.8.8/i686-linux/asm vers /usr/lib/perl5/site_perl/5.8.8/i686-linux/asm-generic, mais les premiers warnings sont toujours là, et je n'aime pas trop garder ce genre de bricolage...

Je précise quand même que :

Je suis en ~arch

Mon système vient d'être totalement émergé pour/avec gcc-4.2/glibc2.6

En utilisant gnu hashstyle et binutils-2.17.50.0.16

Oui je sais j'attire sans doute les problèmes avec tout çà, mais je ne suis pas sur gentoo pour rien  :Wink: 

Si quelqu'un a une idée sur l'origine du problème, ou tout du moins sur l'ebuild responsable de cette erreur...Last edited by razer on Thu Aug 30, 2007 8:06 pm; edited 1 time in total

----------

## Bob_Le_Mou

Salut,

Mes connaissance en perl sont peu rouillées, et je pense que tu dois travailler vraiment "bas niveau" avec perl, parce que je n'ai jamais utilisé "require" de cette façon, d'ailleurs je lui préfère "use".

Ensuite, lorsque j'inclus un module ([...]/toto/titi.pm) c'est toujours du genre use/require toto::titi ...

Enfin, corrige moi, si je me trompe, un handler c'est un handle ? 

Et si oui, pourquoi tu n'utilises pas IO::handle ?

Sinon, je ne vois pas...

----------

## razer

->@Bob_Le_Mou

Merci de ta réponse.

Mes lignes de code sont largement inspirées de la doc officielle de perl (qui précisait d'ailleurs d'être prudent avec ioctl   :Embarassed: )

Je vais revoir mes notes

----------

## Bob_Le_Mou

Ok, j'ai regardé ton projet: vraiment trés interressant...

Je pense que tu devrais jeté un coup d'oeil à IO::Pipe, qui est une sous classe de IO::Handle.

AMHA, en utilisant des headers perl "bas niveau", tu risques de rencontrer pas mal de problèmes, même si c'est techniquement très intéressant, je pense que c'est moins "portable", mais... Ce n'est que mon avis.

/edit

après consultation de pas mal de docs, je crois que la consultation de la doc "perlipc" pourra t'interresser, notamment le paragraphe Bidirectional Communication with Yourself :

```
perldoc perlipc
```

J'espère que çà pourra t'aider.

/edit

----------

## razer

Merci pour ton aide et ton encouragement concernant mon projet.

Ma principale source de documentation est justement celle que tu cites.

L'usage de ioctl m'est nécessaire pour intercepter l'arrivée de nouvelles données dans le handle du processus fils de mon programme. Le but est de mettre à jour en temps réel une fenêtre gtk avec l'avancement des opérations effectuées (resizing d'images, création d'une archive...)

En clair, je fais comme çà (j'imagine que tu as un peu regardé mon code) :

```
# Forking main process in read handle and catch end of child

   $pid = open (CHLDLOG,"-|") ;

   $SIG{CHLD} = \&catch_child;

   # Child make core stuff

   if ($pid == 0) {

      # Open Handle for writing in autoflush mode

      my $childlog = select CHLDLOG; $| = 1; select ($childlog);

                ////

                je code ce que fait le process fils

               ////

      # Very important exit : catching end process doesn't work without

      exit 0;

      }
```

Il y a ensuite dans le process principal une routine exécutée toutes les 150ms par une glib::timeout, et qui se charge d'intercepter les nouveaux messages dans le handle quand ils existent :

```
# Redraw progress status 

sub get_progress {

   # We do something only if processing

   if ($processing == TRUE) {

      # Get a message from child handle if something new happend

      my ($chldmsg, $size, @msg);

      $size = pack("L", 0);

          ioctl(CHLDLOG, FIONREAD(), $size) or die "Couldn't call ioctl: $!\n";

          $size = unpack("L", $size);

      if ($size) {

         sysread (CHLDLOG, $chldmsg, $size);

         # Set gtk label with new message

         @msg = split ("\n", $chldmsg);

         foreach (@msg) {

            if ($_ =~ m/GTKMSG_/) {

               $_ =~ s/GTKMSG_//g;

               $progress_bar->set_text("$_");

               printf "GTKMSG : $_\n";

               }

            else { printf "$_\n" }

            }

         }

      $progress_bar->pulse();

      }

   # Very important

   return (1);

   }
```

Les lignes importantes :

$size = pack("L", 0);

ioctl(CHLDLOG, FIONREAD(), $size) or die "Couldn't call ioctl: $!\n";

$size = unpack("L", $size);

$size est <> 0 lorqu'un nouveau message arrive, je n'ai pas réussi à trouver une technique différente pour réaliser çà, mais j'avoue ne pas tout comprendre à 100% dans la doc. que je lis...

Le reste de la routine me permet simplement de faire le tri entre les messages destinés à l'interface (précédés de GTKMSG) et les infos de debugging.

En plus des problèmes d'intégration de code C qu'implique l'usage de ioctl, la stratégie n'est pas parfaite : j'ai des messages dupliqués, provenant sans doute d'un remplissage du handle lorsque la dernière routine est en cours d'exécution. Cela m'impose d'ajouter des "sleep" dans mon programme fils, et bien sûr c'est moche, bourrin, pas du tout optimisé.

L'idéal serait de créer une interruption provoquée lorsque que le tube reçoit de nouvelles données, et d'exécuter alors une routine ultra simple sans risque de doublons.

Mais encore une fois, je débute en programmation   :Embarassed: 

Il me faut encore un peu de temps pour digérer tous ces concepts, ce projet me permet de le faire de manière ludique et utile  :Smile: 

----------

## Bob_Le_Mou

En fait, je ne connait pas trés bien la bibliothèque GTK2, pas plus le modules perl qui l'exploite.

Ceci dit, j'ai une nette préférence pour l'utilisation de threads pour la programmation de tâche parallèles.

Ce lien pourra te donner quelques indications pour la programmation des threads. 

Mais je dois avouer que je bloque pour ton problème.

----------

## razer

 *Bob_Le_Mou wrote:*   

> En fait, je ne connait pas trés bien la bibliothèque GTK2, pas plus le modules perl qui l'exploite.
> 
> Ceci dit, j'ai une nette préférence pour l'utilisation de threads pour la programmation de tâche parallèles.
> 
> Ce lien pourra te donner quelques indications pour la programmation des threads. 
> ...

 

Toutes ces informations sont très intéressantes, merci à toi.

J'avais déjà en fait eu la curiosité de lire le chapitre sur les threads dans la doc officielle de perl : le lien que tu cites semble être un bon exemple concrêt de leur application avec les GUI.

Malgré le peu de recul à l'état actuel de mon apprentissage, j'estime (naïvement peut être) qu'il vaut mieux apprendre à bien maîtriser les processus avant d'attaquer les threads, je vais donc persister dans ma voie encore un peu avant de franchir le pas.

Le module perl proposé dans le lien et son exemple d'utilisation a biensûr pris place sur mon disque

Encore merci

----------

## Bob_Le_Mou

En fait, d'après ce que j'ai compris, les threads sont en quelque sorte des forks de "fork"... 

Mais bon, je comprend bien que tu n'aies pas envie de réecrire cette partie de ton projet.

Existe-t-il un Ebuild de ton projet quelque part. Afin que je teste si j'ai le même problème ici, je suis en ARCH ?

----------

## avendesora

Salut,

ESt-ce que tu as essayé avec l'approche O_NONBLOCK?

En gros comme ceci:

```

#! /usr/bin/perl -w

use strict;

use warnings;

# pour la constant EAGAIN

use POSIX qw(:errno_h);

# pour la constant O_NONBLOCK

use Fcntl;

open(STATUS, "./slow.sh 2>&1 |") || die "can't fork: $!";

my $flags = $flags = fcntl(STATUS, F_GETFL, 0) || die "Peut pas lire les flags: $!";

fcntl(STATUS, F_SETFL, $flags| O_NONBLOCK) || die "Peut pas mettre nonblock: $!";

my $stuff;

my $cont = 1;

while ($cont) {

        my $ret = read(STATUS, $stuff, 128);

        if (!$ret && $! == EAGAIN) {

                # ici: rien a lire sur le FD, mais le FD est pas ferme

                # on attend un peu, ou on passe au FD suivant, ou ...

                print "waiting\n";

                sleep(1);

        } elsif (!$ret) {

                # ici: le FD est sans doute mort (ferme).

                print "out: $!\n";

                $cont = 0;

        } else {

                # reussi a lire au moins 1 byte

                print "Lu:   $stuff   --\n";

        }

}

close STATUS;

```

C'est un peu plus compliqué à gérer vu que tu ne lis que ce qui est dispo dans le buffer au moment

de l'appel, donc il faut gerer les lectures incompletes. Mais il y a de bonnes chances que ce soit plus

portable que les ioctl.

----------

## Bob_Le_Mou

Le code suivant, me donne le même résultats ( warnings et erreur ) que toi, et je suis en ARCH :

```

#!/usr/bin/perl -w

#

use warnings; use strict;

require "sys/ioctl.ph";

print "test\n";

exit 0;

```

On peut éliminer le problème de ~ARCH

----------

## razer

 *Bob_Le_Mou wrote:*   

> 
> 
> Existe-t-il un Ebuild de ton projet quelque part. Afin que je teste si j'ai le même problème ici, je suis en ARCH ?

 

Non : pour l'instant c'est un tarball qui ne requiert qu'une install de type user (le tout s'installe dans ~/.gnome2/mailpictures)

Des script d'install et d'uninstall sont fournis et c'est ici

Merci pour ton test en ARCH, ce que je sais pour en avoir fait l'usage dans un précédent projet que ce problème ioctl est nouveau...

@mseigneurin

Merci pour ta contribution, je digère actuellement tes lignes de code   :Rolling Eyes: 

----------

## avendesora

C'est pas bien compliqué et ca collerait bien avec ce que tu as déjà.

Le principe c'est qu'un "read" sur un fd en mode nonblock ne bloque jamais.   :Shocked: 

S'il y a des choses à lire, il te les donne.

S'il y a rien, il rend tout de suite la main et positionne errno à EAGAIN (essaye-encore-une-fois).

Il te suffirait de faire les fnctl après que tu aies "forké" (dans le père), et dans ton

get_process, tu fais juste:

```

my $size = read(CHLDLOG, $chldmsg, 4096);

if ($size >0) {

  # y'a eu des donnes => gtk_machin

} elsif ($! == EAGAIN) {

  # le fils a rien dit, mais il tourne encore => progess_bar->clignotte("rouge");

} else {

  # le fils est terminé => progess_bar->tout_vert();

}

```

----------

## razer

 *mseigneurin wrote:*   

> C'est pas bien compliqué et ca collerait bien avec ce que tu as déjà.
> 
> Le principe c'est qu'un "read" sur un fd en mode nonblock ne bloque jamais.  
> 
> S'il y a des choses à lire, il te les donne.
> ...

 

Merci bcp pour ton aide, je vais essayer çà dès que j'aurais un moment.

Juste un truc qui m'intrigue :

```
my $flags = $flags = fcntl(STATUS, F_GETFL, 0) || die "Peut pas lire les flags: $!"; 
```

équivalent à :

```
my $flags= fcntl(STATUS, F_GETFL, 0) || die "Peut pas lire les flags: $!"; 
```

Je présume   :Question:   :Question: 

----------

## Bob_Le_Mou

Une solution à ce problème peut aussi être d'utiliser les possibilités offerte par le module Gtk2::Helper qui va te permettre de faire gérer par GTK2, l'accès et l'attente en sortie du pipe par la création d'un watch spécifique.

Malheureusement je ne peux pas tester ce code (pour l'instant), aussi, tu peux consulter ce lien , vers la fin du code inclus dans le paragraphe : GUI for converting flash videos in xvid dans lequel il y a un exemple de son utilisation.

\edit

Ce lien donne une autre manière permettant d'effectuer de la mise à jour de widget en temps réel, il y est expliqué que Gtk2::Helper ne serait qu'un wrapper de Glib::IO et l'auteur du thread y donne un exemple...

edit\

----------

## avendesora

 *razer wrote:*   

>  *mseigneurin wrote:*   
> 
> ```
> my $flags = $flags = fcntl(STATUS, F_GETFL, 0) || die "Peut pas lire les flags: $!"; 
> ```
> ...

 

Tu présumes bien... ca sent le copier/co'coller bégayeur... désolé.

----------

## razer

De retour après quelques jours de vacances au bout du monde, je suis rassuré par la richesse des posteurs de ce forum.

J'ai vaguement testé la technique de mseigneurin sur mon laptop dans l'avion sans parvenir à éliminer un méchant plantage du process

Je manquais biensûr à ce moment de sources de doc et d'attention.

La piste de Bob_Le_Mou semble aussi intéressante, et même si rien n'est encore fonctionnel sur mon programme sans ioctl, je pense avoir suffisament de possibilités pour classer "résolu" ce thread.

Je vous informerais bien sûr lors de mes prochains progrets

Merci à vous

----------

## Bob_Le_Mou

Salut,

Un exemple en perl/Tk pas finalisé du tout qui peut (mal) illustrer le principe, ici, on utilise la méthode fileevent qui va "surveiller" le handle passé en paramètre et exécuter un callback:

$mw->fileevent($H, 'readable', [\&fill_text_widget, $t]);

On associe au widget $mw l'event file associé au filehandle $H lorsque celui-ci devient "lisible" executer "fill_text_widget($t)" ...

Le principe est quasi le même pour Gtk2::Helper->add_watch ...

Procédure appelée "compteur.pl" :

```
#!/usr/bin/perl -w

#

use strict; use warnings;

use IO::Handle;

STDOUT->autoflush(1);

for my $i (1..15)

{

   sleep 1;

   print "$i) I print this $i\n";

}

exit 0;
```

Procédure appelante : extkfilevent.pl

```

#!/usr/bin/perl

use strict; use warnings;

use IO::Handle;

use Tk;

my $H=IO::Handle->new;

open($H, "./compteur.pl |") or die "Nope: $!";

my $mw=new MainWindow;

$H->autoflush(1);

my $t = $mw->Text(-width => 80, -height => 25, -wrap => 'none');

$t->pack(-expand => 1);

$mw->fileevent($H, 'readable', [\&fill_text_widget, $t]);

MainLoop;

 

sub fill_text_widget {

 

    my($widget) = @_;

 

    my($stat, $data);

    $stat = sysread $H, $data, 4096;

    die "sysread error:  $!" unless defined $stat;

    $widget->insert('end', $data);

    $widget->yview('end');

 

}
```

Sur ce, bon courage...

----------

## razer

Encore merci, la solution Glib::IO->add_watch fonctionne parfaitement.

C'est nettement plus propre et efficace, de nombreuses lignes de code en moins (dont le require à l'origine du problème), et cela crée bien une interruption comme je le souhaitais.

J'ai maintenant une solution générique pour les barres de progression

----------

## Bob_Le_Mou

Salut,

Pourrais-je (entre)voir le code de ton projet ( ou du moins la partie concernant ce problème) ? 

Parce que je suis curieux... Et j'ai un peu de mal avec les processus & les widgets...

\edit

Dans mon premier Post, je t'avais conseillé d'utiliser IO::Handle : ce n'est pas bien, le développeur de cette classe semble nous en décourager fortement : 

 *Quote:*   

>  "IO::Handle" is the base class for all other IO handle classes. It is
> 
>        not intended that objects of "IO::Handle" would be created directly,
> 
>        but instead "IO::Handle" is inherited from by several other classes in
> ...

 

J'ai tendance à faire confiance aux devs.

Et OUI, c'est vrai, j'avoue, je suis un GRAND fan du perl...

edit\

----------

