如何在 Android 中创建命名管道 (mkfifo)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2740321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:10:28  来源:igfitidea点击:

How to create named pipe (mkfifo) in Android?

androidandroid-ndknamed-pipes

提问by Ignas Limanauskas

I am having trouble in creating named pipe in Android and the example below illustrates my dilemma:

我在 Android 中创建命名管道时遇到问题,下面的示例说明了我的困境:

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

The code always prints:

代码总是打印:

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

I can't figure out exactly why this fails. The application has android.permission.WRITE_EXTERNAL_STORAGE permissions. I can create normal files with exactly the same name in the same location, but pipe creation fails. The pipe in question should be accessible from multiple applications.

我无法弄清楚为什么会失败。该应用程序具有 android.permission.WRITE_EXTERNAL_STORAGE 权限。我可以在同一位置创建具有完全相同名称的普通文件,但管道创建失败。有问题的管道应该可以从多个应用程序访问。

  1. I suspect that noone can create pipes in /sdcard. Where would it be the best location to do so?
  2. What mode mast should I set (2nd parameter)?
  3. Does application need any extra permissions?
  1. 我怀疑没有人可以在 /sdcard 中创建管道。这样做的最佳地点在哪里?
  2. 我应该设置什么模式桅杆(第二个参数)?
  3. 应用程序是否需要任何额外的权限?

回答by fadden

Roosmaa's answeris correct -- mkfifo() just calls mknod() to create a special file, and FAT32 doesn't support that.

Roosmaa 的回答是正确的——mkfifo() 只是调用 mknod() 来创建一个特殊文件,而 FAT32 不支持。

As an alternative you may want to consider using Linux's "abstract namespace" UNIX-domain sockets. They should be roughly equivalent to a named pipe. You can access them by name, but they're not part of the filesystem, so you don't have to deal with various permission issues. Note the socket is bi-directional.

作为替代方案,您可能需要考虑使用 Linux 的“抽象名称空间”UNIX 域套接字。它们应该大致相当于命名管道。您可以按名称访问它们,但它们不是文件系统的一部分,因此您不必处理各种权限问题。请注意,套接字是双向的。

Since it's a socket, you may need INTERNET permission. Not sure about that.

由于它是一个套接字,您可能需要 INTERNET 权限。不确定。

Here's a quick bit of client/server sample code:

这是客户端/服务器示例代码的快速位:

#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] = '
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;
}
'; /* 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; }

回答by Mart Roosmaa

The default filesystem of /sdcard is FAT32, which doesn't support named pipes.

/sdcard 的默认文件系统是 FAT32,它不支持命名管道。

On a non-rooted device the only possible place you could try to create those pipes would be the application data directory /data/data/com.example/ . Note: You shouldn't hardcode that value, use the Context.getApplicationInfo().dataDir .

在非 root 设备上,您可以尝试创建这些管道的唯一可能位置是应用程序数据目录 /data/data/com.example/ 。注意:您不应该对该值进行硬编码,请使用 Context.getApplicationInfo().dataDir 。

But be aware that whenever the user is using Apps2SD or whenever Google implements that support officially you need to make sure to let the user know that the app can't be stored on vfat files system.

但请注意,每当用户使用 Apps2SD 或 Google 正式实施该支持时,您都需要确保让用户知道该应用程序无法存储在 vfat 文件系统中。

回答by Stéphane

there's also /sqlite_stmt_journals(we use it for testing, I don't know how long this directory will survive OS updates)

还有/sqlite_stmt_journals(我们用它来测试,我不知道这个目录在操作系统更新后还能存活多久)

If you need IPC, the best practices are to use the Binders

如果您需要 IPC,最佳实践是使用Binder

If you only need inter-thread communication, you can use unnamed pipes through JNI (this works fine)

如果您只需要线程间通信,您可以通过 JNI 使用未命名管道(这很好用)

回答by EntangledLoops

I would like to append to the accepted answer:

我想附加到接受的答案:

1) I am able to use this method to connect a socket between two native modules of an Android app.

1) 我能够使用这种方法在 Android 应用程序的两个本机模块之间连接套接字。

2) write()should be in a loop since it may not write the full amount requested in the first go. For example, it should read something like:

2)write()应该处于循环中,因为它可能不会写入第一次请求的全部金额。例如,它应该是这样的:

##代码##

The error handling shown above is insufficient, since several error codes simply indicate to try again. See the documentation for write.

上面显示的错误处理是不够的,因为几个错误代码只是指示重试。请参阅write的文档。

回答by Jesse Wilson

If you're coding this in Java, you should just use PipedInputStreamand PipedOutputStream.

如果您使用 Java 编写此代码,您应该只使用PipedInputStreamPipedOutputStream