2010-04-29 16 views
11

Tengo problemas en la creación de canalización con nombre en Android y el siguiente ejemplo ilustra mi dilema:¿Cómo crear named pipe (mkfifo) en Android?

res = mkfifo("/sdcard/fifo9000", S_IRWXO); 
if (res != 0) 
{ 
    LOG("Error while creating a pipe (return:%d, errno:%d)", res, errno); 
} 

El código siempre se imprime:

Error while creating a pipe (return:-1, errno:1) 

no puedo averiguar exactamente por qué esto no funciona. La aplicación tiene permisos android.permission.WRITE_EXTERNAL_STORAGE. Puedo crear archivos normales con exactamente el mismo nombre en la misma ubicación, pero la creación de tuberías falla. El tubo en cuestión debe ser accesible desde múltiples aplicaciones.

  1. Sospecho que nadie puede crear tuberías en/sdcard. ¿Dónde sería la mejor ubicación para hacerlo?
  2. ¿Qué mástil de modo debo configurar (2º parámetro)?
  3. ¿La aplicación necesita permisos adicionales?

Respuesta

14

Roosmaa's answer es correcto - mkfifo() simplemente llama a mknod() para crear un archivo especial, y FAT32 no lo admite.

Como alternativa, puede considerar utilizar los zócalos de dominio UNIX de "espacio de nombres abstracto" de Linux. Deberían ser más o menos equivalentes a una tubería con nombre. Puede acceder a ellos por su nombre, pero no son parte del sistema de archivos, por lo que no tiene que lidiar con varios problemas de permisos. Tenga en cuenta que el zócalo es bidireccional.

Dado que es un zócalo, es posible que necesite permiso de INTERNET. No estoy seguro de eso

Aquí hay un poco rápida de código de ejemplo de cliente/servidor:

#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <stddef.h> 
#include <sys/socket.h> 
#include <sys/un.h> 

/* 
* Create a UNIX-domain socket address in the Linux "abstract namespace". 
* 
* The socket code doesn't require null termination on the filename, but 
* we do it anyway so string functions work. 
*/ 
int makeAddr(const char* name, struct sockaddr_un* pAddr, socklen_t* pSockLen) 
{ 
    int nameLen = strlen(name); 
    if (nameLen >= (int) sizeof(pAddr->sun_path) -1) /* too long? */ 
     return -1; 
    pAddr->sun_path[0] = '\0'; /* abstract namespace */ 
    strcpy(pAddr->sun_path+1, name); 
    pAddr->sun_family = AF_LOCAL; 
    *pSockLen = 1 + nameLen + offsetof(struct sockaddr_un, sun_path); 
    return 0; 
} 

int main(int argc, char** argv) 
{ 
    static const char* message = "hello, world!"; 
    struct sockaddr_un sockAddr; 
    socklen_t sockLen; 
    int result = 1; 

    if (argc != 2 || (argv[1][0] != 'c' && argv[1][0] != 's')) { 
     printf("Usage: {c|s}\n"); 
     return 2; 
    } 

    if (makeAddr("com.whoever.xfer", &sockAddr, &sockLen) < 0) 
     return 1; 
    int fd = socket(AF_LOCAL, SOCK_STREAM, PF_UNIX); 
    if (fd < 0) { 
     perror("client socket()"); 
     return 1; 
    } 

    if (argv[1][0] == 'c') { 
     printf("CLIENT %s\n", sockAddr.sun_path+1); 

     if (connect(fd, (const struct sockaddr*) &sockAddr, sockLen) < 0) { 
      perror("client connect()"); 
      goto bail; 
     } 
     if (write(fd, message, strlen(message)+1) < 0) { 
      perror("client write()"); 
      goto bail; 
     } 
    } else if (argv[1][0] == 's') { 
     printf("SERVER %s\n", sockAddr.sun_path+1); 
     if (bind(fd, (const struct sockaddr*) &sockAddr, sockLen) < 0) { 
      perror("server bind()"); 
      goto bail; 
     } 
     if (listen(fd, 5) < 0) { 
      perror("server listen()"); 
      goto bail; 
     } 
     int clientSock = accept(fd, NULL, NULL); 
     if (clientSock < 0) { 
      perror("server accept"); 
      goto bail; 
     } 
     char buf[64]; 
     int count = read(clientSock, buf, sizeof(buf)); 
     close(clientSock); 
     if (count < 0) { 
      perror("server read"); 
      goto bail; 
     } 
     printf("GOT: '%s'\n", buf); 
    } 
    result = 0; 

bail: 
    close(fd); 
    return result; 
} 
+0

esto puede ser difícil de replicar en Java - simplemente diciendo :) – KevinDTimm

+1

Es cierto, pero el fragmento de código original tampoco es Java, así que pensé que no era un factor decisivo. El enfoque de socket es ciertamente más intensivo en JNI que un canal con nombre en el sistema de archivos. – fadden

+0

Derecha, el objetivo es C. Este enfoque parece requerir un hilo separado por cliente conectado. –

8

El sistema de archivos predeterminado de/sdcard es FAT32, que no admite canalizaciones con nombre.

En un dispositivo no rooteado, el único lugar posible donde podría intentar crear esos conductos sería el directorio de datos de la aplicación /data/data/com.example/. Nota: No debe codificar ese valor, use Context.getApplicationInfo(). DataDir.

Pero tenga en cuenta que cada vez que el usuario utiliza Apps2SD o cada vez que Google implemente oficialmente ese soporte, debe asegurarse de informarle que la aplicación no se puede almacenar en el sistema de archivos vfat.

1

también hay /sqlite_stmt_journals (lo usamos para las pruebas, no sé cuánto tiempo este directorio sobrevivirá actualizaciones del sistema operativo)

Si necesita IPC, son las mejores prácticas para utilizar la Binders

Si sólo necesita la comunicación entre hilos, puede utilizar tubos no identificadas a través de JNI (esto funciona bien)

+0

Sí, esto es lo que terminé usando. La clave aquí es conseguir que el código nativo se comunique con el código nativo. Revisar Java lo haría más complicado (JNI y similares) y potencialmente más lento. La interfaz de Binder está disponible en C++, pero se necesita una fuente completa de Android, lo cual es un gran obstáculo. –

+0

Utilizamos JNI en las tuberías sin nombre porque tenemos código Java que envía datos de manera asíncrona a nuestros hilos nativos; pero si solo tiene que hacer que sus hilos nativos hablen con otros hilos nativos dentro del mismo proceso, no necesita JNI para eso. Debo añadir que las tuberías son más rápidas y ligeras que las tomas. –

0

Si está codificando esto en Java, solo debe usar PipedInputStream y PipedOutputStream.

+0

pueden utilizarse PipedInputStream y PipedOutputStream en múltiples aplicaciones, como en C++, un namedPipe puede ser utilizado por 2 aplicaciones diferentes para lectura y escritura. –

+1

No. Está en proceso solamente. –

1

me gustaría añadir a la respuesta aceptada:

1) Soy capaz de utilizar este método para conectar un conector entre dos módulos nativos de una aplicación para Android.

2) write() debe estar en un bucle ya que puede no escribir la cantidad completa solicitada en el primer intento. Por ejemplo, debe decir algo como:

void *p = buffer; 
count = 0; 
while ((count += write(clientSock, buffer, num_bytes - count)) < num_bytes) 
{ 
    if (count < 0) 
    { 
     close(clientSock); 
     errCode = count; 
     break; 
    } 
    p += count; 
} 

El manejo de errores se muestra arriba es insuficiente, ya que varios códigos de error indican simplemente para volver a intentarlo. Consulte la documentación para write.

+0

Puede usar 'TEMP_FAILURE_RETRY()' para volver a intentar en EINTR. – fadden