Forum Programmation.c Programmer un outil d'impression...

Posté par  .
Étiquettes : aucune
0
2
jan.
2006
C'est toujours moi...
Bon, suite à mon message ici : http://linuxfr.org/forums/19/13887.html
je me suis posé la question suivante : pourquoi dans la version originelle ai-je fait appel à lp ?
Ben oui, c'est con : une simple boucle qui lit les caractères un par un dans le fichier et qui les rebalance au fur et à mesure à l'imprimante devrait résoudre mon problème....
En relisant mon code (je vous en fait grâce, il est gore), je me suis apperçu qu'a l'époque, je l'avait fait, et ça m'avait posé problème....
En effet, le code ressemble à ça :

*c=fgetc(in);
while (!feof (in))
{
fprintf (lp, "%c", c);
c=fgetc (in);
}
fclose (in);

in est un pointeur sur le fichier à lire, et lp un pointeur sur /dev/lp0 en écriture.
Ca fonctionne, ce n'est pas le problème. Ce qui me gêne, c'est que c'est bloquant : si le fichier est gros, il faut attendre que l'imprimante ait fini l'édition. Quand il y a 20 000 pages (oui, ça arrive), faut pas être pressé.
Il y a évidement moyen d'optimiser ça en lisant le fichier ligne par ligne, ou même blocs par blocs, mais ça ne résoud que partiellement le problème.
En cherchant un peu dans la doc fournie avec ma distro (en particulier les man pages), je me suis apperçu que j'avais peut-être la solution avec les threads....
Mais voilà, je ne suis pas assez calé pour coder ça. Alors si une bonne âme pouvait me donner un exemple de thread créé dans le main, qui continue de s'exécuter en arrière plan, y compris lorsque le main est mort, je lui en serait reconnaissant.
A la rigueur, je ne demande pas tout mon programme (faut bien que je me garde quelques lignes à taper quand même), mais juste un exemple avec un pauvre appel à une fonction foo dans le thread me suffirait.
Merci mille fois par avance.
  • # var/spool et daemon.

    Posté par  . Évalué à 2.

    Pour l'arrière-plan, tu cherches « daemonize » sur le Grand Ternet :

    http://www.google.fr/search?hl=fr&q=Unix+daemonize&b(...)

    Sinon pour le reste, il y a le répertoire /var/spool qui permet de stocker temporairement les fichiers à envoyer en série vers la file d'impression.
  • # Bout de code

    Posté par  . Évalué à 3.

    Pour 'daemonizer' le processus courant, j'utilise ce bout de code (le boolean en paramètre sert à indiquer si on doit faire un double fork ou pas -- en pratique, si tu le lances pas avec init faut le mettre à true):

    ------------------------------8<--------------------------------

    static void daemonize(int dofork)
    {
    /** @brief This daemonizes the current process */

    pid_t pid;
    struct rlimit limit;

    /* Set to background */
    if (dofork || getppid() != 1)
    {
    pid = fork();

    switch(pid)
    {
    case -1: /* Error */
    perror(ERR(daemonize, fork)); exit(1);
    break;
    case 0: /* Success */
    break;
    default: /* Kill the father */
    exit(0);
    }

    /* Run the processus in a new session */
    setpgid(0, 0);

    pid = fork();

    switch(pid)
    {
    case -1: /* Error */
    perror(ERR(daemonize, fork)); exit(1);
    break;
    case 0: /* Success */
    break;
    default: /* Kill the father */
    exit(0);
    }

    /* Ignore terminal signals */
    if (signal(SIGTTIN, SIG_IGN) == SIG_ERR)
    {
    perror(ERR(daemonize, signal)); exit(1);
    }
    if (signal(SIGTTOU, SIG_IGN) == SIG_ERR)
    {
    perror(ERR(daemonize, signal)); exit(1);
    }
    if (signal(SIGTSTP, SIG_IGN) == SIG_ERR)
    {
    perror(ERR(daemonize, signal)); exit(1);
    }
    if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
    {
    perror(ERR(daemonize, signal)); exit(1);
    }
    }

    /* Change directory to root */
    if (chdir("/") != 0) { perror(ERR(daemonize, chdir)); exit(1); }

    /* Get the maximum file descriptor */
    if(getrlimit(RLIMIT_NOFILE, & limit) == -1)
    {
    perror(ERR(daemonize, getrlimit)); exit(1);
    }

    /* Close all the file descriptors */
    while (limit.rlim_cur --) { close(limit.rlim_cur); }
    }

    ------------------------------8<--------------------------------

    Sinon, pour ta boucle de lecture de fichier, tu pourrais utiliser fread/fwrite, ou mapper le fichier en mémoire, ou encore (dans ton premier post tu compte utiliser un socket) utiliser un socket, et la super fonction sendfile() :)

    Exemples basiques:

    avec fread/frwrite, ça donne ça:

    ------------------------------8<--------------------------------

    unsigned char buffer[BUFSIZ];
    for ( ;; ) {
        fread(buffer, sizeof(char), sizeof(buffer), in);
    if (! feof)
            fwrite(buffer, sizeof(char), sizeof(buffer), fp);
        else
            break;
    }

    ------------------------------8<--------------------------------

    mais c'est un euphémisme de dire que c'est pas optimal :)

    exemple naïf avec mmap():

    ------------------------------8<--------------------------------

    unsigned char *map = NULL;
    int in = 0;
    int lp = 0;
    struct stat infos;
    size_t taille_fichier = 0;

    if ( (in = open(ton_fichier, O_RDONLY)) == -1) {
        perror(ERR(lecture, open)); abort(); /* gestion d'erreur basique :] */
    }

    if ( (lp = open(imprimante, O_WRONLY)) == -1) {
    perror(ERR(lecture, open)); abort(); /* gestion d'erreur basique :] */
    }

    /* récupère la taille du fichier à balancer */
    if (fstat(in, & infos) == -1)
        perror(ERR(lecture, fstat));
    else
        /* on récupère juste la taille, mais on pourrait vérifier
         * que c'est bien un fichier régulier, toussa...
         */
        taille_fichier = infos.st_size;

    /* mappe le fichier en mémoire (gros tableau) */
    if ( (map = mmap(NULL, taille_fichier, PROT_READ, MAP_PRIVATE, in, 0) == MAP_FAILED) {
        perror(ERR(lecture, mmap)); abort();
    }

    /* oh le gros write de bourrin :) */
    write(fd, map, taille_fichier);

    ------------------------------8<--------------------------------

    Et le must, le gros sendfile() de sauvage, ultra pas portable mais si bourrin :)

    ------------------------------8<--------------------------------

    [Coller ici le même code que pour mmap, mais sans ouvrir lp]

    au lieu d'appeler mmap(), et en supposant que tu as un socket connecté à ton imprimante nommé 'bull':

    ssize_t ret = 0;
    if ( (ret = sendfile(bull, in, NULL, taille_fichier)) == -1)
        perror(ERR(lecture, sendfile));
    else
        printf("youpi, sendfile a envoyé %z bytes dans les tuyaux\n", ret);

    ------------------------------8<--------------------------------

    Et là, enjoy, c'est le noyau qui se tape tout le boulot, et en zero-copy s'il vous plaît :)

    * la macro ERR() qui apparait un peu partout c'est un truc maison pour afficher la ligne du code qui foire:

    #define STRINGIFY(x) #x
    #define STR(x) STRINGIFY(x)
    #define ERR(c, f) #c "()::" #f "() @ " __FILE__ ":" STR(__LINE__)

    Voilà voilà :)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.