SDK Multiplataforma en C logo

SDK Multiplataforma en C

Procesos

❮ Anterior
Siguiente ❯

Un proceso es un programa en ejecución que es lanzado y controlado por el sistema operativo.


Funciones

Proc*bproc_exec (...)
voidbproc_close (...)
bool_tbproc_cancel (...)
uint32_tbproc_wait (...)
bool_tbproc_finish (...)
bool_tbproc_read (...)
bool_tbproc_eread (...)
bool_tbproc_write (...)
bool_tbproc_read_close (...)
bool_tbproc_eread_close (...)
bool_tbproc_write_close (...)
voidbproc_exit (...)

Desde la perspectiva del programador, el multi-procesamiento es la posibilidad de iniciar e interactuar con otros procesos (hijos) desde proceso principal (padre). El sistema operativo puede ejecutar el proceso hijo en otro núcleo de la CPU (true multitasking) o en el mismo que el padre (context switch). Esta es una decisión del sistema en la que el programador no puede influir que dependerá del tipo de procesador y de su carga de trabajo. El efecto final será que ambos procesos (padre e hijo) se ejecutan en paralelo.


1. Lanzando procesos

bproc_exec lanzará un proceso desde nuestro propio programa en C de forma similar a como lo hace el Terminal (Figura 1). En este caso, la E/S Estándar stdin, stdout y stderr se redireccionará al objeto Proc mediante tuberías anónimas. Desde aquí, podemos utilizar bproc_write para escribir en el canal stdin del hijo y bproc_read para leer desde su stdout. Las reglas de lectura/escritura son las que rigen los pipes del sistema operativo y que podemos resumir en:

Esquema de un proceso padre y un proceso hijo unidos mediante el objeto Proc.
Figura 1: Lanzando un proceso desde nuestro propio código C.
  • Si el padre llama a bproc_read y el hijo no ha escrito nada (búfer vacío), el padre se bloqueará (esperará) hasta que haya información en el canal de salida del hijo.
  • Si el hijo termina y el padre está esperando para leer, bproc_read devolverá FALSE y el padre continuará su ejecución.
  • Si el padre llama a bproc_write y el búfer de escritura está lleno, el padre se bloqueará (esperará) hasta que el hijo lea desde su stdin y libere espacio en el canal.
  • Si el hijo termina y el padre está bloqueado por escritura, bproc_write devolverá FALSE y el padre continuará su ejecución.
  • Algunos comandos o procesos (p.e sort) no se iniciarán hasta que se lea todo el contenido de stdin. En estos casos, el proceso padre deben usar bproc_write_close para indicar al hijo que la escritura en su stdin ha concluido.
  • Cuando el padre llama a bproc_close, todos los canales de E/S se cerrarán y ambos procesos continuarán su ejecución de forma independiente. Para terminar la ejecución del proceso hijo (kill) utiliza bproc_cancel.
  • bproc_wait detendrá al proceso padre hasta que el hijo finalice. Para evitar la sobrecarga del buffer de salida stdout del hijo, cerrar el canal mediante bproc_read_close.
  • bproc_finish comprobará, de forma no bloqueante, si el hijo ha terminado su ejecución.

2. Ejemplos multi-procesamiento

Veamos algunos ejemplos prácticos de IPC Inter-Process Communication utilizando los canales E/S estándar en procesos vinculados padre-hijo. En (Listado 1) volcaremos en un fichero la salida stdout del proceso hijo. En (Listado 2) redirigiremos ambos canales, escribiremos en stdin y leeremos de stdout apoyándonos en archivos de disco. Por último, implementaremos un protocolo asíncrono donde el padre y el hijo intercambian peticiones y respuestas. En (Listado 4) mostramos el código del proceso hijo, en (Listado 3) el del proceso padre y en (Listado 5) el resultado de la comunicación, escrito por el proceso padre.

Listado 1: Leyendo del stdout de un proceso y guardándolo en un archivo.
1
2
3
4
5
6
7
8
byte_t buffer[512];
uint32_t rsize;
File *file = bfile_create("out.txt", NULL);
Proc *proc = bproc_exec("dir C:\Windows\System32", NULL, NULL);
while(bproc_read(proc, buffer, 512, &rsize, NULL) == TRUE)
    bfile_write(file, buffer, rsize, NULL, NULL);
bproc_close(&proc);
bfile_close(&file);
Los comandos del shell no son portables en general. Los utilizamos solo como ejemplo.
Listado 2: Redirigiendo el stdin y stdout de un proceso.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
byte_t buffer[512];
uint32_t rsize;
File *fsrc = bfile_open("members.txt", ekFILE_READ, NULL);
File *fdes = bfile_create("sorted_members.txt", NULL);
Proc *proc = bproc_exec("sort", NULL, NULL);

// Writes to stdin
while (bfile_read(fsrc, buffer, 512, &rsize, NULL) == TRUE)
    bproc_write(proc, buffer, rsize, NULL, NULL);

// Closes child stdin
bproc_write_close(proc);

// Reads child stdout
while(bproc_read(proc, buffer, 512, &rsize, NULL) == TRUE)
    bfile_write(fdes, buffer, rsize, NULL, NULL);

bfile_close(&fsrc);
bfile_close(&fdes);
bproc_close(&proc);
Listado 3: Protocolo asíncrono (proceso padre).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
Proc *proc;
uint32_t commands[] = { 326, 32, 778, 123, 889, 712, 1, 55, 75, 12 };
uint32_t exit_command = 0;
uint32_t i;

proc = bproc_exec("child", NULL, NULL);

for (i = 0; i < 10; ++i)
{
    uint32_t response;
    uint32_t time;
    // Send command to child
    bproc_write(proc, (byte_t*)&commands[i], sizeof(uint32_t), NULL);

    // Waits for child response
    bproc_read(proc, (byte_t*)&response, sizeof(uint32_t), NULL);
    bproc_read(proc, (byte_t*)&time, sizeof(uint32_t), NULL);
    bstd_printf("Child command %d in %d milliseconds.\n", response, time);
}

bproc_write(proc, (byte_t*)&exit_command, sizeof(uint32_t), NULL);
bproc_close(&proc);
Listado 4: Protocolo asíncrono (proceso hijo).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for (;;)
{
    uint32_t command;
    // Reads from standard input a command from parent.
    if (bstd_read((byte_t*)&command, sizeof(command), NULL) == TRUE)
    {
        if (command != 0)
        {
            // Waits random time (simulates processing).
            uint32_t timer = bmath_randi(1000, 2000);
            bthread_sleep(timer);

            // Writes to standard output the response to parent.
            bstd_write((const byte_t*)&command, sizeof(command), NULL);
            bstd_write((const byte_t*)&timer, sizeof(timer), NULL);
        }
        else
        {
            // Command 0 = Exit
            break;
        }
    }
}
Listado 5: Resultado de la ejecución del proceso padre.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Child command 326 in 1761 milliseconds.
Child command 32 in 1806 milliseconds.
Child command 778 in 1989 milliseconds.
Child command 123 in 1909 milliseconds.
Child command 889 in 1043 milliseconds.
Child command 712 in 1153 milliseconds.
Child command 1 in 1780 milliseconds.
Child command 55 in 1325 milliseconds.
Child command 75 in 1157 milliseconds.
Child command 12 in 1426 milliseconds.

bproc_exec ()

Lanza un nuevo proceso.

Proc*
bproc_exec(const char_t *command,
           const ProcOpt *opts,
           perror_t *error);
command

El comando a ejecutar (ruta y argumentos). p.e. "ls -lh" o "C:\Programs\imgresize background.png -w640 -h480".

opts

Opciones de creación del proceso.

error

Código de error si la función falla. Puede ser NULL.

Retorna

Manejador del proceso hijo que podemos utilizar para comunicarnos con él. Si la función falla, retorna NULL.

Observaciones

Ejemplos multi-procesamiento.


bproc_close ()

Cierra la comunicación con el proceso hijo y libera recursos.

void
bproc_close(Proc **proc);
proc

Manejador del proceso. Será puesto a NULL tras el cierre.

Observaciones

Si el proceso todavía se está ejecutando, esta función no lo finaliza. Solo cierra el canal de comunicación entre el padre y el hijo que continuarán ejecutándose de forma independiente. Como cualquier otro objeto, un proceso siempre debe cerrarse, incluso si ya ha terminado su ejecución. Ejemplos multi-procesamiento.


bproc_cancel ()

Fuerza la finalización del proceso.

bool_t
bproc_cancel(Proc *proc);
proc

Manejador del proceso.

Retorna

TRUE si el proceso ha terminado. FALSE si no.


bproc_wait ()

Espera hasta que el proceso hijo finalice.

uint32_t
bproc_wait(Proc *proc);
proc

Manejador del proceso.

Retorna

El valor de retorno del proceso hijo o UINT32_MAX si hay algún error.


bproc_finish ()

Comprueba si el proceso hijo sigue en ejecución.

bool_t
bproc_finish(Proc *proc,
             uint32_t *code);
proc

Manejador del proceso.

code

El valor de salida del proceso (si ha terminado). Puede ser NULL.

Retorna

TRUE si el proceso hijo ha terminado, FALSE si no.

Observaciones

Esta función retorna inmediatamente. No bloquea al proceso que la llama.


bproc_read ()

Lee datos desde la salida estándar del proceso (stdout).

bool_t
bproc_read(Proc *proc,
           byte_t *data,
           const uint32_t size,
           uint32_t *rsize,
           perror_t *error);
proc

Manejador del proceso.

data

Búfer donde se escribirán los datos leídos.

size

El número de bytes máximos a leer (tamaño del buffer).

rsize

Recibe el número de bytes leídos realmente. Puede ser NULL.

error

Código de error si la función falla. Puede ser NULL.

Retorna

TRUE si se han leído datos. FALSE si ha ocurrido algún error.

Observaciones

Esta función bloqueará al proceso padre hasta que el hijo escriba en su stdout. Si no hay datos en el canal y el hijo termina, retornará FALSE con rsize = 0 y error = ekPROC_SUCCESS. Ejemplos multi-procesamiento.


bproc_eread ()

Lee datos desde la salida de error del proceso (stderr).

bool_t
bproc_eread(Proc *proc,
            byte_t *data,
            const uint32_t size,
            uint32_t *rsize,
            perror_t *error);
proc

Manejador del proceso.

data

Búfer donde se escribirán los datos leídos.

size

El número de bytes máximos a leer.

rsize

Recibe el número de bytes leídos realmente. Puede ser NULL.

error

Código de error si la función falla. Puede ser NULL.

Retorna

TRUE si se han leído datos. FALSE si ha ocurrido algún error.

Observaciones

Esta función bloqueará al proceso padre hasta que el hijo escriba en su stdout. Si no hay datos en el canal y el hijo termina, retornará FALSE con rsize = 0 y error = ekPROC_SUCCESS. Ejemplos multi-procesamiento.


bproc_write ()

Escribe datos en el canal de entrada del proceso (stdin).

bool_t
bproc_write(Proc *proc,
            const byte_t *data,
            const uint32_t size,
            uint32_t *wsize,
            perror_t *error);
proc

Manejador del proceso.

data

Búfer que contiene los datos a escribir.

size

El número de bytes a escribir.

wsize

Recibe el número de bytes escritos realmente. Puede ser NULL.

error

Código de error si la función falla. Puede ser NULL.

Retorna

TRUE si se han escrito datos. FALSE si ha ocurrido algún error.

Observaciones

Esta función bloqueará al proceso padre si no hay espacio en el búfer para completar la escritura. Cuando el proceso hijo lea si stdin y libere espacio, se completará la escritura y el proceso padre continuará su ejecución. Ejemplos multi-procesamiento.


bproc_read_close ()

Cierra el canal stdout del proceso hijo.

bool_t
bproc_read_close(Proc *proc);
proc

Manejador del proceso.

Retorna

TRUE si el canal se ha cerrado. FALSE si ya estaba cerrado.

Observaciones

Esta función permite ignorar la salida del proceso hijo, previniendo bloqueos por la saturación del canal. Lanzando procesos.


bproc_eread_close ()

Cierra el canal stderr del proceso hijo.

bool_t
bproc_eread_close(Proc *proc);
proc

Manejador del proceso.

Retorna

TRUE si el canal se ha cerrado. FALSE si ya estaba cerrado.

Observaciones

Esta función permite ignorar la salida de errores del proceso hijo, previniendo bloqueos por la saturación del canal. Lanzando procesos.


bproc_write_close ()

Cierra el canal stdin del proceso hijo.

bool_t
bproc_write_close(Proc *proc);
proc

Manejador del proceso.

Retorna

TRUE si el canal se ha cerrado. FALSE si ya estaba cerrado.

Observaciones

Algunos procesos necesitan leer todo el contenido de stdin antes de comenzar el trabajo. Al cerrar el canal, el proceso hijo recibe la señal EOF End-Of-File en stdin. Lanzando procesos.


bproc_exit ()

Termina el proceso actual (el que llama) y todos sus hijos de ejecución.

void
bproc_exit(const uint32_t code);
code

El código de salida del proceso.

❮ Anterior
Siguiente ❯