- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
你好。
我正在尝试通过 execve 或 execl 函数在 C fork() 之后在 Raspberry Pi 上执行 omxplayer ( http://elinux.org/Omxplayer ),以便我可以保存视频播放过程的 PID(因此系统不会执行工作)。如果我在 X 控制台/终端上执行该程序,它可以工作,但如果它通过标准终端(不启动 X),它将运行但如果在子进程上调用 execve 则不会将视频输出到屏幕。顺便说一句,通过控制台中的“omxplayer ...”命令执行播放器将播放视频并输出到屏幕。我对这类事情有点陌生,所以这是我无法解决或找到答案的情况。这里的任何人都对如何解决这个问题有想法或指导我找到可能的解决方案?
注意:该代码只是一个 execve 调用,我知道它是正确的,因为在 X 中它工作得很好。
最佳答案
execve()
call 为执行的程序提供了一个新的环境。对于能够访问 X 显示的程序,您至少需要保留某些环境变量 -- DISPLAY
。您是否在新环境中不小心遗漏了 DISPLAY
?
为了 OMXPlayer 在没有 X 的情况下工作,它必须能够访问视频设备本身(/dev/video
,在这种情况下;参见 OMXPlayer builds 页面了解详细信息)。它通常配置为允许 video
组的所有成员访问它。
您可以在您的程序中使用 popen("id -Gn", "r")
来运行列出当前组成员的 id -Gn
命令。 (从文件句柄中以字符串形式读取列表,然后使用 pclose()
将其关闭。)如果列表不包含 video
,则问题在于权限运行原始程序的用户的权限不包括对视频设备的访问权限。解决方法很简单:将 video
添加到用户所属的组中。
这是一个示例程序,run.c
,用于说明execvp()
的基本用法:
#include <unistd.h>
/* For the example main(): */
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Try executing a command in a child process.
* Returns the PID of the child process,
* but does not tell whether the execution was
* successful or not.
* Returns (pid_t)-1 with errno set if fork() fails.
*/
pid_t run(char *const command[])
{
pid_t child;
child = fork();
if (child == (pid_t)-1)
return (pid_t)-1;
if (!child) {
execvp(command[0], command);
_exit(127);
}
return child;
}
int main(int argc, char *argv[])
{
pid_t child, p;
int status;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s COMMAND [ ARGUMENTS .. ]\n", argv[0]);
fprintf(stderr, "\n");
return 1;
}
child = run(argv + 1);
if (child == (pid_t)-1) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return 1;
}
fprintf(stderr, "(%s: PID %d)\n", argv[1], (int)child);
fflush(stderr);
do {
p = waitpid(child, &status, 0);
if (p == (pid_t)-1 && errno == EINTR)
continue;
} while (p != child && p != (pid_t)-1);
if (p == (pid_t)-1) {
fprintf(stderr, "(%s: %s.)\n", argv[1], strerror(errno));
return 1;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 127)
fprintf(stderr, "(%s: Could not execute command.)\n", argv[1]);
else
if (WEXITSTATUS(status) == 0)
fprintf(stderr, "(%s: Exited successfully.)\n", argv[1]);
else
fprintf(stderr, "(%s: Exited with error %d.)\n", argv[1], WEXITSTATUS(status));
} else
if (WIFSIGNALED(status))
fprintf(stderr, "(%s: Killed by %s.)\n", argv[1], strsignal(WTERMSIG(status)));
else
fprintf(stderr, "(%s: Died from unknown causes.)\n", argv[1]);
return status;
}
您可以使用例如编译和测试它
gcc -W -Wall -O3 run.c -o run
./run date --utc
请注意,run()
函数不会尝试检查命令是否实际执行;它只返回子进程 PID,如果 fork()
失败,则返回 (pid_t)-1
。
许多实现,包括 GNU C 库 popen()
,使用 127 退出状态作为执行失败的指示。即不是应该执行的命令返回的,而是子进程返回的,因为命令执行失败。上面的 run()
也是这样做的。
可以在run()
函数中的父子进程之间使用close-on-exec管道,让父进程知道子进程是否成功启动了想要的命令,如果没有,为什么不呢。然后父进程也可以立即获取已失效的子进程。如果出现错误,这会给调用者带来很少的额外工作,因此我个人强烈推荐这种方法。这是一个示例实现:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
/* Helper function: Close file descriptor, without modifying errno.
* Returns 0 if successful, otherwise the errno reported by close().
*/
static int closefd(const int fd)
{
int saved_errno, result;
/* Invalid descriptor? */
if (fd == -1)
return EINVAL;
/* Save errno. It's thread-local, so as long as we restore
* it before returning, no-one will notice any change in it. */
saved_errno = errno;
/* Close descriptor, and save errno (or 0) in result. */
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
if (result == -1)
result = errno;
else
result = 0;
/* Restore errno. Done. */
errno = saved_errno;
return result;
}
/* Helper function: Create a close-on-exec pipe.
* Return 0 if success, errno otherwise.
*/
int close_on_exec_pipe(int fds[2])
{
int result;
result = pipe(fds);
if (result == -1) {
fds[0] = -1;
fds[1] = -1;
return errno;
}
do {
do {
result = fcntl(fds[0], F_SETFD, FD_CLOEXEC);
} while (result == -1 && errno == EINTR);
if (result == -1)
break;
do {
result = fcntl(fds[1], F_SETFD, FD_CLOEXEC);
} while (result == -1 && errno == EINTR);
if (result == -1)
break;
/* Success. */
return 0;
} while (0);
/* Failed. */
closefd(fds[0]);
closefd(fds[1]);
fds[0] = -1;
fds[1] = -1;
return errno;
}
/* Run an external command in a child process.
* command[0] is the path or name of the command,
* and the array must be terminated with a NULL.
*
* If successful, this function will return the PID
* of the child process. Otherwise, it will return
* (pid_t)-1, with errno indicating the error.
*/
pid_t run(char *const command[])
{
pid_t child, p;
int commfd[2], errcode;
/* Create a close-on-exec pipe between the parent and child. */
if (close_on_exec_pipe(commfd))
return (pid_t)-1;
/* Fork the new child process. */
child = fork();
if (child == (pid_t)-1) {
closefd(commfd[0]);
closefd(commfd[1]);
return (pid_t)-1;
}
if (!child) {
/* Child process: */
/* Close the read/parent end of the pipe. */
closefd(commfd[0]);
/* In case of C library bugs, prepare errno. */
errno = EINVAL;
/* Execute the desired command. */
execvp(command[0], command);
/* Failed. errno describes why. */
errcode = errno;
/* Send it to the parent via the pipe. */
{
const char *p = (char *)&errcode;
const char *const q = (char *)&errcode + sizeof errcode;
ssize_t n;
while (p < q) {
n = write(commfd[1], p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
break;
else
if (errno != EINTR)
break;
}
}
/* Close write/child end of the pipe. */
closefd(commfd[1]);
/* Exit with a failure (127). */
_exit(127);
}
/* Parent: */
/* Close the write/child end of the pipe. */
closefd(commfd[1]);
/* Try to read the execution error. */
{
char *p = (char *)&errcode;
char *const q = (char *)&errcode + sizeof errcode;
ssize_t n;
errcode = 0;
while (p < q) {
n = read(commfd[0], p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
break; /* n == 0 is pipe closed */
else
if (errno != EINTR)
break;
}
/* Close the read/parent end of the pipe. */
closefd(commfd[0]);
/* Pipe closed (on exec), no data read? */
if (n == (ssize_t)0 && p == (char *)&errcode) {
/* Yes, success! */
errno = 0;
return child;
}
/* Execution failed.
* If we didn't get the reason, use EINVAL. */
if (!errcode || p != q)
errcode = EINVAL;
}
/* Reap the child process. */
do {
p = waitpid(child, NULL, 0);
if (p == (pid_t)-1) {
if (errno == EINTR)
continue;
else
break;
}
} while (p != child);
/* Return with failure. */
errno = errcode;
return (pid_t)-1;
}
在我看来,这种方法的唯一缺点是在父进程中使用了额外的两个描述符,尽管只是暂时的。在几乎所有情况下,这都是无关紧要的,但如果您有一个使用大量文件描述符的服务器类型应用程序,则您应该注意这一点。
Phidgets 库使用线程。执行回调的线程与 RFID Phidgets 示例中等待按键的线程不同。一种选择是使用 posix_spawn()
执行播放器(从非主线程)。
但是,一般来说,最好让主线程同时使用 waitpid(child, &status, WNOHANG)
监控播放器。检查玩家是否退出,并处理任何新的 RFID 事件,根据需要启动玩家(如果有新 RFID,则杀死现有实例),如果 RFID 移出阅读器范围,甚至杀死玩家。
这需要一个简单的线程事件队列:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
/* RFID tag event types: tag read, tag lost.
*/
typedef enum {
RFID_TAG_LOST = 0,
RFID_TAG_READ
} rfid_event_type_t;
/* Structure describing all possible RFID tag events.
*/
typedef struct rfid_event_st rfid_event_t;
struct rfid_event_st {
struct rfid_event_st *next;
rfid_event_type_t type;
CPhidgetRFIDHandle rfid;
CPhidgetRFID_Protocol protocol;
void *userptr;
char tag[];
};
static pthread_mutex_t event_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t event_wait = PTHREAD_COND_INITIALIZER;
static rfid_event_t *event_queue = NULL;
/* Add event to event queue.
*/
static int add_event(const CPhidgetRFIDHandle rfid,
const CPhidgetRFID_Protocol protocol,
const rfid_event_type_t type,
const char *const tag,
void *const userptr)
{
const size_t taglen = (tag) ? strlen(tag) : 0;
rfid_event_t *ev;
/* Allocate memory for a new event. */
ev = malloc(sizeof (rfid_event_t) + taglen + 1);
if (!ev)
return errno = ENOMEM;
/* Fill in the fields. */
ev->next = NULL;
ev->type = type;
ev->rfid = rfid;
ev->protocol = protocol;
ev->userptr = userptr;
if (taglen > 0)
memcpy(ev->tag, tag, taglen);
ev->tag[taglen] = '\0';
/* Lock event queue. */
pthread_mutex_lock(&event_lock);
/* Append to the event queue. */
if (event_queue) {
rfid_event_t *prev = event_queue;
while (prev->next)
prev = prev->next;
prev->next = ev;
} else
event_queue = ev;
/* Signal and unlock. */
pthread_cond_signal(&event_wait);
pthread_mutex_unlock(&event_lock);
return 0;
}
/* Get next event, waiting at most 'maxwait' seconds.
*/
static rfid_event_t *get_event(const long maxwait)
{
struct timespec until;
rfid_event_t *ev;
pthread_mutex_lock(&event_lock);
/* Event already in the queue? */
if (event_queue) {
ev = event_queue;
event_queue = ev->next;
ev->next = NULL;
pthread_mutex_unlock(&event_lock);
return ev;
}
/* No waiting requested? */
if (maxwait <= 0L) {
pthread_mutex_unlock(&event_lock);
return NULL;
}
/* Get current wall clock time, */
clock_gettime(CLOCK_REALTIME, &until);
/* and add maxwait seconds. */
until.tv_sec += maxwait;
/* Wait for a signal. */
pthread_cond_timedwait(&event_wait, &event_lock, &until);
/* Event arrived in the queue? */
if (event_queue) {
ev = event_queue;
event_queue = ev->next;
ev->next = NULL;
pthread_mutex_unlock(&event_lock);
return ev;
}
/* No event; timed out. */
pthread_mutex_unlock(&event_lock);
return NULL;
}
根据 Phidgets RFID 示例,标签和标签丢失处理程序是
int CCONV TagHandler(CPhidgetRFIDHandle RFID, void *usrptr, char *TagVal, CPhidgetRFID_Protocol proto)
{
return add_event(RFID, proto, RFID_TAG_READ, TagVal, usrptr);
}
int CCONV TagLostHandler(CPhidgetRFIDHandle RFID, void *usrptr, char *TagVal, CPhidgetRFID_Protocol proto)
{
return add_event(RFID, proto, RFID_TAG_LOST, TagVal, usrptr);
}
不是在所有设置完成后等待按键,而是创建一个循环,类似于
pid_t child = (pid_t)-1; /* Not running */
pid_t p;
rfid_event_t *event;
/* Infinite loop */
while (1) {
/* Do we have a player child process? */
if (child != (pid_t)-1) {
/* Yes. Has it exited yet? */
p = waitpid(child, NULL, WNOHANG);
if (p == child) {
/* Yes. No more player. */
child == (pid_t)-1;
}
}
/* Check for a new event.
* If we have a child, only wait one second only
* for the event; otherwise, wait up to 30 secs.
*/
if (child == (pid_t)-1)
event = get_event(30L);
else
event = get_event(1L);
/* If no event yet, start at the beginning of the loop. */
if (!event)
continue;
/*
* TODO: Handle the event.
* You can stop the existing player via e.g.
* if (child != (pid_t)-1)
* kill(child, SIGKILL);
* and then start a new one.
*/
/* Discard the event. It's dynamically allocated. */
free(event);
}
如果您启动播放器,上面的循环会在一秒钟内检测到它没有播放。如果没有玩家在运行,那么循环等待 RFID 信号的时间就可以了——我用了 30 秒。
关于c - 在 C 程序上通过 execve/l 执行 omxplayer 不会在 fork() 之后在子进程的非 X 控制台上输出视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17400954/
话说,尾部的++在这里没有实际作用? 最佳答案 l+l++ 未定义。您的表达式中没有序列点来分隔对 l 的访问和后增量。它可以做任何事情,包括具有与 l+l 相同的效果。 编辑:问题和答案在 Why
我正在研究成员资格算法,我正在研究这个特定问题,该问题说明如下: 展示一种算法,给定任何常规语言 L,确定 L 是否 = L* 所以,我的第一个想法是,我们有 L*,它是 L 的 Kleene 星并确
我试图弄清楚如何使用 Javascript 生成一个随机 11 个字符串,该字符串需要特定的字母/数字序列,以及位置。 ----------------------------------------
我一直在 LinqPad 中试验查询。我们有一个表 Lot,其中有一列 Side char(1)。当我编写 linq to sql 查询 Lots.Where(l => l.Side == 'A')
这个问题在这里已经有了答案: Iterate over all pairs of consecutive items in a list [duplicate] (7 个答案) 关闭 7 年前。 假
列表 ['a','a #2','a(Old)'] 应变为 {'a'} 因为 '# ' 和 '(Old)' 将被删除,并且不需要重复项列表。我努力用生成器开发列表理解,并决定这样做,因为我知道它会起作用
我正在为蛇和梯子制作一 block 板,到目前为止,我已经按降序打印了板。但是,我需要以正确的方式打印电路板。 编辑“螺旋下降”意味着 100...91 81...90 80...71 ...
字符串“Hello\n”等于 {'H','e','l','l','o','\','n','\0'} 或 {'H','e','l','l','o','\n','\0'}? 是否在字符串定义中添加转义序列
这个问题在这里已经有了答案: Different behaviour for list.__iadd__ and list.__add__ (3 个答案) 关闭 8 年前。 ls = [1,2,3]
当我在编写一个程序时,我在我的代码中看到了一个奇怪的行为。这是我所看到的。 >>> l = [1,2,3,4,5,6,7,8] >>> g = [] >>> for i in l: ... g
我明白了what a Y Combinator is , 但我不明白这个来自 Wikipedia page 的“新颖”组合子的例子: Yk = (L L L L L L L L L L L L L
Exception ParseException is not compatible with throws clause in Comparator.compare(L, L). 我在java 6上
期望的输出 我想要一个函数返回一个列表,这样,给定一个“困惑的”列表 l,每个元素都是 l 对应元素的索引,如果 l 已排序。 (抱歉,我想不出更简单的说法。) 示例 f([3,1,2]) = [2,
你好,我正在查看“假设一个排序数组在你事先不知道的某个枢轴旋转。(即 0 1 2 4 5 6 7 可能变成 4 5 6 7 0 1 2)”这个问题的 C++ 解决方案。你如何有效地在旋转数组中找到一个
让我们考虑这个简单的例子: import numpy as np a=np.arange(90) a=a.reshape(6,3,5) 我想得到一个数组 b形状 (6*5,3+1=4) 与 b[0:6
我正在编写一个 q 脚本,它在特定路径中加载一个数据库并对其进行一些处理。 db 的位置目前在脚本中是硬编码的,但我想将 db 路径作为参数传递并让它从变量中的路径加载。 目前它看起来像这样: q)
为什么我收到错误 Device: (3:9741) (0,l.useLinkBuilder) is not a function。 (在 '(0,l.useLinkBuilder)()' 中,'(0,
我有 ADT 版本 23.0.4 并安装了 Android 5.0 的 SDK 平台。 我读到 Android 5.0 Lolipop 的 API 级别为 21。但是在 Eclipse 的“新建应用程
我在 Google Play Store 中实现了一个抽屉导航,我想在 DrawerLayout 中设置列 TableView 的选定项目。但是后来发现在touch模式下无法选中item,有一个i
作为 C++ 的新手,我基本上有一个关于 g++ 编译器的问题,尤其是库的包含。考虑以下生成文件: CPPFLAGS= -I libraries/boost_1_43_0-bin/include/ -
我是一名优秀的程序员,十分优秀!