Autor Tema: Procesos linux - Exit y Wait  (Leído 21280 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Procesos linux - Exit y Wait
« en: 19 de Abril de 2008, 16:01:45 »
Hola

Estoy estudiando cómo matar procesos hijos y cuando mueren cómo leer un valor desde el proceso padre. Apenas estoy conociendo las funciones exit y wait y la de wait me tiene muy confundido.

El programa que no me gusta cómo funciona es el siguiente...

Código: [Seleccionar]
/* Este programa hace que un hijo haga una suma de numeros pasados por parametro para
 que posteriormente, cuando el hijo muera, devuelva al programa padre la suma de dichos numeros */
 
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

main(int argc,char *argv[])
{
            int num,estado;

            switch(fork())
            {
                        case 0:
                        {
                        /* El hijo calcula la operación y lo devuelve con el exit() */
     printf("HIJO %d %d \n",atoi(argv[1]),atoi(argv[2]));
                                     num=atoi(argv[1]) + atoi(argv[2]);
     printf("HIJO %d %d\n",num,(char)num);
                                     exit(num);
                        }           
                        default:
                        {
                        /* El padre recibe con el wait lo devuelto por el hijo en el exit y lo imprime por pantalla */                                    
                                     wait(&estado);
     printf("PADRE %0x %d\n",&estado,estado);
                                     printf("\nPADRE la suma de los hijos es: %d, %d\n",estado/256,estado);
     printf("PADRE %lu\n",sizeof(estado));
                        }
            }
}

Ejecutando el programa en el bash de linux pongo:

Código: [Seleccionar]
$ ./programa 10 10

Le entrego 10 y 10 y debe hacer la suma de 20 y el padre reconocerla como 20, pero los resultados en pantalla son los siguientes...

Código: [Seleccionar]
HIJO 10 10
HIJO 20 20
PADRE bfbe9538 5120

PADRE la suma de los hijos es: 20, 5120
PADRE 4


Supuestamente exit entrega los 8 bits menos significativos del int llamado num. Luego wait los recibe y lo guarda en estado, pero he aquí el dilema, no recibe 20, recibe la cantidad de 5120, que es como si multiplicara 20 x 256. Por eso es que unas líneas después corrijo el número dividiéndolo entre 256.

La pregunta... ¿por qué se multiplica el número por 256 (o se rota 8 bits a la izquierda) al moverlo de un hijo a un padre?  :(
« Última modificación: 19 de Abril de 2008, 16:10:08 por migsantiago »

Desconectado ma4826

  • PIC16
  • ***
  • Mensajes: 130
Re: Procesos linux - Exit y Wait
« Respuesta #1 en: 19 de Abril de 2008, 16:40:40 »
Con estos cambios funciona correctamente:

Código: [Seleccionar]
/* Este programa hace que un hijo haga una suma de numeros pasados por parametro para
 que posteriormente, cuando el hijo muera, devuelva al programa padre la suma de dichos numeros */
 
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

main(int argc,char *argv[])
{
            int num,estado,error;

            switch(fork())
            {
                        case 0:
                        {
                        /* El hijo calcula la operación y lo devuelve con el exit() */
     printf("HIJO %d %d \n",atoi(argv[1]),atoi(argv[2]));
                                     num=atoi(argv[1]) + atoi(argv[2]);
     printf("HIJO %d %d\n",num,(char)num);
                                     exit(num);
                        }           
                        default:
                        {
                        /* El padre recibe con el wait lo devuelto por el hijo en el exit y lo imprime por pantalla */                                     
                                     wait(&estado);
     error=WEXITSTATUS(estado);
     printf("PADRE %0x %d\n",&estado,estado);
                                     printf("\nPADRE la suma de los hijos es: %d, %d\n",estado/256,error);
     printf("PADRE %lu\n",sizeof(estado));
                        }
            }
}

Tienes que usar WEXITSTATUS para obtener el código de error que devuelve el proceso al salir.

Saludos,

Miguel Angel.


万人の友は誰の友でもない。

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #2 en: 19 de Abril de 2008, 17:37:18 »
Muchas gracias Miguel Angel, eso que me acabas de enseñar ni mi maestro supo explicarlo  :D

El programa ya funcionando es:

Código: [Seleccionar]
/* Este programa hace que un hijo haga una suma de numeros pasados por parametro para
 que posteriormente, cuando el hijo muera, devuelva al programa padre la suma de dichos numeros */
 
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

main(int argc,char *argv[])
{
            int num,estado;
            int error;

            switch(fork())
            {
                        case 0:
                        {
                        /* El hijo calcula la operación y lo devuelve con el exit() */
     printf("HIJO %d %d \n",atoi(argv[1]),atoi(argv[2]));
                                     num=atoi(argv[1]) + atoi(argv[2]);
     printf("HIJO %d \n",num);
                                     exit(num);
                        }           
                        default:
                        {
                        /* El padre recibe con el wait lo devuelto por el hijo en el exit y lo imprime por pantalla */                                     
                                     wait(&estado);
                                    error=WEXITSTATUS(estado); //entrega los 8 bits más bajos de estado
                                     printf("\nPADRE la suma de los hijos es: %d\n",error);
                        }
            }
}

 :mrgreen: :mrgreen:

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #3 en: 19 de Abril de 2008, 19:01:19 »
Hola de nuevo

Me dejaron de tarea lo siguiente...

Investigar cuáles son los tipos de señales POSIX y escribir 2 programas que de ejemplo.

Hasta ahora sólo he leído unos ejemplos de la función...

Código: [Seleccionar]
#include <signal.h>
int kill (pid_t pid,int sig)

Y las señales que linux maneja son las siguientes...



Al usar 2 de esas señales con kill, ¿ya estaría cumpliendo con mi tarea? Es que no conozco bien eso del POSIX y no quiero equivocarme.  :oops:

Gracias

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #4 en: 22 de Abril de 2008, 20:10:36 »
Hola

Aparte de la pregunta de arriba, tengo otra pregunta. Escribí un programa en linux que indica si un proceso existe en ejecución o no. El programa al terminar de ejecutarse entrega un 0 si el proceso existe o un 1 si no existe.

Lo entrega usando un
Código: [Seleccionar]
exit(); al final del programa. Luego viene mi duda, tengo que hacer un shell script que haciendo uso de mi programa, indique al usuario si el proceso existe, leyendo la salida de exit.

Intenté hacer el shell script de la siguiente manera...

Código: [Seleccionar]
#!/bin/bash
salida=` ./existe 876`
echo salida = $salida

En donde " ./existe 876" es el programa y recibe 876 como el proceso a buscar. Pero lo que se deposita en salida no es la salida de exit del programa existe, es un revoltijo de caracteres de salida, pero nada que ver con salida uno o cero.

¿Qué puedo usar para leer la salida del programa en el shell?  :roll:

Gracias

PD Adjunto el código del programa existe.c por si sirve para ayudarme =)

Edito:

Perdón ya lo encontré en internet...

http://www.comp.nus.edu.sg/~tanyeefa/cs1102c/mini0002.html

El script modificado queda así...

Código: [Seleccionar]
#!/bin/bash
./existe 22774
salida=$?
echo salida = $salida

 :mrgreen:
« Última modificación: 22 de Abril de 2008, 20:32:39 por migsantiago »

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #5 en: 24 de Abril de 2008, 20:51:19 »
Bueno, ya regresé con otra súper duda.

Estoy resolviendo un examen y una pregunta dice...

Escriba un programa que reciba como  argumento una dirección IP y envíe una
llamada broadcast a toda la red solicitando la dirección MAC asociada a la IP indicada
como argumento. El host cuya dirección IP coincida con la petición enviada, contestará
con su dirección MAC. El programa deberá  de recibir el mensaje de respuesta y
mostrará la dirección MAC por la salida estándar (pantalla).

Lo que tengo que usar es un socket y mucho ingenio, porque hasta ahora no he encontrado el cómo hacerlo. Investigué mucho sobre cómo se obtiene la MAC conociendo la IP, se debe usar el protocolo ARP, pero todo lo que encontré fue teoría y solo un programa...

Código: [Seleccionar]
   #include <stdlib.h>
    #include <string.h>
#include <stdio.h>
#include <errno.h> 
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h> 
#include <netinet/in.h>
#include <netinet/if_ether.h>
  #include <netdb.h>
 
int _get_MAC(char *addr,char *dport) {
  int sock,sockfd, n;
  char buffer[2048];
    unsigned char *iphead, *ethhead;
    struct hostent *he;
           struct sockaddr_in their_addr; // connector's address information
// struct ifreq iface;
 // struct sockaddr_in *addr;
  struct ether_addr ether;
 

  if ( (sock=socket(PF_PACKET, SOCK_RAW,htons(ETH_P_IP)))<0) {//
    perror("socket");
    exit(1);
  }
   
     
    if ((he=gethostbyname(addr)) == NULL) {  // get the host info
            perror("gethostbyname");
            exit(1);
    }
      if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

        their_addr.sin_family = AF_INET;    // host byte order
        their_addr.sin_port = htons(atoi(dport));  // short, network byte order
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        memset(&(their_addr.sin_zero), '\0', 8);  // zero the rest of the struct

        if (connect(sockfd, (struct sockaddr *)&their_addr,
                                              sizeof(struct sockaddr)) == -1) {
            perror("connect");
            exit(1);

while (1) {
if (n = recvfrom(sock,buffer,2048,0,NULL,NULL)== -1) {
    perror("recvfrom");
     close(sock);
            exit(1);
}
 
// n = recv(sock,buffer,2048,0);
    ethhead = buffer;
    if (ethhead != NULL)
              {
     
    printf("Source MAC address: "
           "%02x:%02x:%02x:%02x:%02x:%02x\n",
           ethhead[0],ethhead[1],ethhead[2],
           ethhead[3],ethhead[4],ethhead[5]);
    printf("Destination MAC address: "
           "%02x:%02x:%02x:%02x:%02x:%02x\n",
           ethhead[6],ethhead[7],ethhead[8],
           ethhead[9],ethhead[10],ethhead[11]); 
              }
       iphead = buffer+14; /* Skip Ethernet header */
    if (*iphead==0x45) { /* Double check for IPv4
                          * and no options present */
      printf("Source host %d.%d.%d.%d\n",
             iphead[12],iphead[13],
             iphead[14],iphead[15]);
      printf("Dest host %d.%d.%d.%d\n",
             iphead[16],iphead[17],
             iphead[18],iphead[19]);
      printf("Source,Dest ports %d,%d\n",
             (iphead[20]<<8)+iphead[21],
             (iphead[22]<<8)+iphead[23]);
      printf("Layer-4 protocol %d\n",iphead[9]);
    }
 }   
 return 0;
}


int main(int argc, char **argv)
{
    // cc=argv[1];
    _get_MAC(argv[1],argv[2]);
      return 0;
}

El programa compila correctamente con unas modificaciones pequeñas, pero no se ejecuta bien ya que el parámetro SOCK_RAW del primer socket es de nivel root y pues no me da chance.

¿Alguien conoce un método fácil para resolver mi problema?

Está muy difícil la pregunta del examen ya que el maestro no da bien las clases, pero bueno, doy por hecho que esa ya la tuve mal. Al menos ya resolví 5 de 6 preguntas  :mrgreen:

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Probar programa
« Respuesta #6 en: 01 de Mayo de 2008, 11:45:35 »
Hola

El maestro ya nos dio la solución al problema...

Código: [Seleccionar]
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define ETH "eth0"

struct msgARP
{
unsigned char destinoEthernet[6];
unsigned char origenEthernet[6];
unsigned short tipoEthernet;
unsigned short tipoHardware;
unsigned short protocolo;
unsigned char longitudMAC;
unsigned char longitudRED;
unsigned short tipoARP;
unsigned char origenMAC[6];
unsigned char origenIP[4];
unsigned char destinoMAC[6];
unsigned char destinoIP[4];
};

int main (int argc,char *argv[])
{
struct msgARP msg;
struct ifreq ifr;
struct sockaddr sa;
int s,optral,n;
unsigned char ip[4];

/*
Se define un socket SOCK_PACKET para que TCP/IP no
incluya información adicional en las cabeceras. Con lo
que se tiene el control de los formatos del mensaje
*/

//Abriendo el socket

if((s=socket(PF_INET,SOCK_PACKET,htons(ETH_P_ARP)))<0)
{
perror("Socket");
exit(1);
}

//permitir el envío broadcast
optral=1;

if(setsockopt(s,SOL_SOCKET,SO_BROADCAST,&optral,sizeof(optral))<0)
{
perror("setsockopt");
exit(1);
}

/*obtención de la dirección IP y MAC de nuestra tarjeta*/

strcpy(ifr.ifr_name,ETH);
if(ioctl(s,SIOCGIFHWADDR,&ifr)<0)
{
perror("ioctl");
exit(1);
}

/*Se copia la dirección MAC al campo correspondiente de la cabecera ARP
y de la trama ethernet
*/

bcopy(&ifr.ifr_hwaddr.sa_data,&msg.origenMAC,6);
bcopy(&ifr.ifr_hwaddr.sa_data,&msg.origenEthernet,6);

/*Se obtiene la dirección IP asociada a la tarjeta de red*/
strcpy(ifr.ifr_name,ETH);

if(ioctl(s,SIOCGIFADDR,&ifr)<0)
{
perror("ioctl");
exit(1);
}

/*Copiar la dirección IP al campo de la cabecera ARP*/
bcopy(&ifr.ifr_addr.sa_data[2],&msg.origenIP,4);

/*Como dirección MAC de destino para la trama Ethernet se establece
   FF:FF:FF:FF:FF:FF para realizar un broadcast*/

memset(&msg.destinoEthernet,0xff,6);
   
/*En la trama Ethernet se indicará que se transporta un mensaje
ARP, para que al recibir la trama cualquier estación destino sepa a
quién enviar los datos en el nivel superior*/
msg.tipoEthernet=htons(ETH_P_ARP);
   
/*Tipo de red Ethernet*/
msg.tipoHardware=htons(ARPHRD_ETHER);
/*El protocolo a nivel de red es IP*/
msg.protocolo=htons(ETH_P_IP);
/*longitud de las direcciones físicas en este caso son 6,
ya que se representan de la forma XX:XX:XX:XX:XX:XX:*/
msg.longitudMAC=6;

/*longitud de las direcciones de red*/
msg.longitudRED=4;

/*tipo de mensaje ARP: solicitud o respuesta*/
msg.tipoARP=htons(ARPOP_REQUEST);

/*la dirección MAC de la máquina destino*/
bzero(&msg.destinoMAC,6);

/*la dirección IP de la máquina destino*/
inet_aton(argv[1],msg.destinoIP);
strncpy(ip,msg.destinoIP,4);
bzero(&sa,sizeof(sa));
strcpy(sa.sa_data,ETH);

if(sendto(s,&msg,sizeof(msg),0,(struct sockaddr *)&sa,sizeof(sa))<0)
    {
    perror("sendto");
    exit(1);
    }
   
do
    {
    bzero(&sa,sizeof(sa));
    bzero(&msg,sizeof(msg));
    n=sizeof(sa);
    if(recvfrom(s,&msg,sizeof(msg),0,(struct sockaddr *)&sa,&n)<0)
        {
        perror("recvform");
        exit(1);
        }
    if((ntohs(msg.tipoARP)==ARPOP_REPLY)&&!strncmp(ip,msg.origenIP,4))
        {
        fprintf(stdout,"Respuesta del equipo al que se llamó, la IP es %s y se corresponde con al MAC %02x:%02x:%02x:%02x:%02x:%02x\n",argv[1],msg.origenMAC[0],msg.origenMAC[1],msg.origenMAC[2],msg.origenMAC[3],msg.origenMAC[4],msg.origenMAC[5]);
        }
    }while(1);
   
    /*se cierra el socket*/
    close(s);
}
   


El programa también compila bien. Pero también manda error de socket, no tengo los permisos para correrlo; lo intenté correr en mi linux como administrador y tampoco dio resultado.

Necesito ayuda de alguien que tenga forma de ejecutarlo, por favor díganme si corre bien. Los pasos para correrlo son...

Código: [Seleccionar]
$ ./macaddress 192.168.1.254

En donde la IP es el equipo de quien quieren obtener la MAC address.

Necesito estar seguro de que el programa corre para aceptar tener mal esa respuesta en el examen, de lo contrario tomaré cartas en el asunto  :mrgreen:

Adjunto el código fuente y el ejecutable. Gracias.

Desconectado ma4826

  • PIC16
  • ***
  • Mensajes: 130
Re: Procesos linux - Exit y Wait
« Respuesta #7 en: 01 de Mayo de 2008, 19:52:29 »
Puse

sudo ./macaddress 192.168.0.1

Y funcionó bien.

Saludos,

Miguel Ángel.



« Última modificación: 01 de Mayo de 2008, 20:00:59 por ma4826 »
万人の友は誰の友でもない。

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #8 en: 01 de Mayo de 2008, 20:52:33 »
Puse

sudo ./macaddress 192.168.0.1

Y funcionó bien.

Saludos,

Miguel Ángel.

Hola Miguel

¿Cómo lo ejecutaste si nadie ha descargado el ejecutable del post de arriba?

Ejecuté lo siguiente y pues no sé para qué sirve el "sudo"

Código: [Seleccionar]
$ sudo ./macaddress localhost

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

Password:
Sorry, try again.
Password:
sudo: 1 incorrect password attempt
[svillafuertea08@cluster16 a08]$

No tengo acceso root al servidor linux  :(

Desconectado ma4826

  • PIC16
  • ***
  • Mensajes: 130
Re: Procesos linux - Exit y Wait
« Respuesta #9 en: 01 de Mayo de 2008, 21:12:14 »
 Cuando lo probé no estaba "identificado" por lo que no vi los adjuntos. Lo que hice fue pegar el código en el gedit, guardarlo como macaddress.c y ejecutar gcc -o macaddress macaddress.c, luego lo probé:

Código: [Seleccionar]
ma4826@maamd:~$ gcc -o macaddress macaddress.c
ma4826@maamd:~$ ./macaddress 192.168.0.1
Socket: Operation not permitted
ma4826@maamd:~$ sudo ./macaddress 192.168.0.1
[sudo] password for ma4826:
Respuesta del equipo al que se llamó, la IP es 192.168.0.1 y se corresponde con al MAC 00:18:e7:f8:18:04

Saludos,

Miguel Angel.




万人の友は誰の友でもない。

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #10 en: 01 de Mayo de 2008, 21:17:22 »
Gracias Miguel

Ahorita regresé de linux en mi pc y corrió muy bien con el sudo, incluso el programa que había publicado anteriormente también corrió muy bien.

De haber sabido que existía el sudo, habría tenido la oportunidad de entregar el programa antes  :8}

Me gustaría haber tenido un mejor profesor de Linux  :?

Desconectado maunix

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 4751
    • Mi Sitio Web Personal
Re: Procesos linux - Exit y Wait
« Respuesta #11 en: 05 de Mayo de 2008, 09:06:36 »
Oh, recién leo este hilo y también estoy dando algunos pasitos, en mis escasos tiempos libres, para aprender C/C++ bajo linux.

De haber sabido que existía el sudo, habría tenido la oportunidad de entregar el programa antes  :8}

De todas formas miguel, el sudo por si solo no sirve para nada si tu usuario no tiene permiso para adquirir permisos de root.  Para ello debe estar en el listado de usuarios que pueden usar sudo o "sudoers list". 

En cuanto a averiguar el Mac Address, tu profesor tal vez debió explicarles el Comando ARP que es independiente del Sistema Operativo . 
- La soberbia de un Einstein es entendible.. la de un salame es intolerable (A.Dolina)
- En teoría no hay diferencia entre la teoría y la práctica. En la práctica... si la hay.
- Lee, Lee, Lee y luego pregunta.(maunix)
- Las que conducen y arrastran al mundo no son las máquinas, sino las ideas (V. Hugo)
- Todos los hombres se parecen por sus palabras; solamente las obras evidencian que no son iguales.(Moliere)
- Todo debería ser hecho tan simple como sea posible pero no mas simple que eso.(A.Einstein)

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #12 en: 05 de Mayo de 2008, 10:40:45 »
Hola Mauricio

El profesor da las clases de manera súper aburrida y dejó el examen con esa pregunta, sin siquiera darnos las bases. Yo investigué todo, incluso como armar el paquete ARP, pero no supe cómo echar a andar el proyecto. También nos dejó otra pregunta del examen... semáforos como mecanismos IPC, pero tampoco la supe hacer.

Estoy aprendiendo mucho por mi cuenta, pero la clase y la experiencia pudo haber sido mejor si el profesor fuera de mejor calidad.

Desconectado maunix

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 4751
    • Mi Sitio Web Personal
Re: Procesos linux - Exit y Wait
« Respuesta #13 en: 05 de Mayo de 2008, 12:06:52 »
Estoy aprendiendo mucho por mi cuenta, pero la clase y la experiencia pudo haber sido mejor si el profesor fuera de mejor calidad.

Oh, por supuesto. Un buen profesor allana terreno, de eso no hay dudas, si solamente se dedica a tirar tarea, para eso que te pase las clases por mail y estudias en tu casa.
- La soberbia de un Einstein es entendible.. la de un salame es intolerable (A.Dolina)
- En teoría no hay diferencia entre la teoría y la práctica. En la práctica... si la hay.
- Lee, Lee, Lee y luego pregunta.(maunix)
- Las que conducen y arrastran al mundo no son las máquinas, sino las ideas (V. Hugo)
- Todos los hombres se parecen por sus palabras; solamente las obras evidencian que no son iguales.(Moliere)
- Todo debería ser hecho tan simple como sea posible pero no mas simple que eso.(A.Einstein)

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Procesos linux - Exit y Wait
« Respuesta #14 en: 05 de Mayo de 2008, 12:55:43 »
jaja

pues el examen fue para hacerse en casa  :D