通过execve/l WIN'在C程序上执行omxplayer;t在fork()之后的子进程上的非X控制台上输出视频
Hy 我试图通过execve或execl函数在C fork()之后对Raspberry Pi执行omxplayer(),这样我就可以为视频播放过程保存PID(这样系统就不会做这项工作)。如果我在X控制台/终端上执行程序,它可以工作,但如果通过标准终端(不启动X),它将运行,但不会将视频输出到屏幕上,如果在子进程上调用Exeve。顺便说一下,通过控制台中的“omxplayer…”命令执行播放器将播放视频并输出到屏幕。我对这类事情有点陌生,所以这是一种我无法解决或找到答案的情况。这里有人对如何解决这个问题有想法,或者给我一个方向,让我找到一个可能的解决方案吗 注意:该代码只是一个execve调用,我知道它是正确的,因为在X中它工作得很好。该调用为执行的程序提供了一个新的环境。为了让程序能够访问X显示屏,您需要至少保留某些环境变量--通过execve/l WIN'在C程序上执行omxplayer;t在fork()之后的子进程上的非X控制台上输出视频,c,linux,video,raspberry-pi,execve,C,Linux,Video,Raspberry Pi,Execve,Hy 我试图通过execve或execl函数在C fork()之后对Raspberry Pi执行omxplayer(),这样我就可以为视频播放过程保存PID(这样系统就不会做这项工作)。如果我在X控制台/终端上执行程序,它可以工作,但如果通过标准终端(不启动X),它将运行,但不会将视频输出到屏幕上,如果在子进程上调用Exeve。顺便说一下,通过控制台中的“omxplayer…”命令执行播放器将播放视频并输出到屏幕。我对这类事情有点陌生,所以这是一种我无法解决或找到答案的情况。这里有人对如何解决这
display
。您是否无意中遗漏了新环境中的显示
要使OMXPlayer在没有X的情况下工作,它必须能够访问视频设备本身(/dev/video
,在本例中;有关详细信息,请参见第页)。它通常配置为允许视频
组的所有成员访问它
您可以在程序中使用popen(“id-Gn”,“r”)
来运行id-Gn
命令,该命令列出了当前的组成员身份。(从文件句柄以字符串形式读取列表,然后使用pclose()
将其关闭)如果列表不包含video
,则问题在于运行原始程序的用户的权限不包括对视频设备的访问。解决方法很简单:将视频添加到用户所属的组中
下面是一个示例程序,run.c
,用于说明execvp()
的基本用法:
请注意,run()
函数不会尝试检查命令是否实际执行;它只返回子进程PID,如果fork()
失败,则返回(PID_t)-1
许多实现,包括gnuclibrarypopen()
,都使用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示例,标记和标记丢失处理程序是
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秒。调用为执行的程序提供了一个新的环境。为了让程序能够访问X显示屏,您需要至少保留某些环境变量--display
。您是否无意中遗漏了新环境中的显示
要使OMXPlayer在没有X的情况下工作,它必须能够访问视频设备本身(/dev/video
,在本例中;有关详细信息,请参见第页)。它通常配置为允许视频
组的所有成员访问它
您可以在程序中使用popen(“id-Gn”,“r”)
来运行id-Gn
命令,该命令列出了当前的组成员身份。(从文件句柄以字符串形式读取列表,然后使用pclose()
将其关闭)如果列表不包含video
,则问题在于运行原始程序的用户的权限不包括对视频设备的访问。解决方法很简单:将视频添加到用户所属的组中
下面是一个示例程序,run.c
,用于说明execvp()
的基本用法:
请注意,run()
函数不会尝试检查命令是否实际执行;它只返回子进程PID,如果fork()
失败,则返回(PID_t)-1
许多实现,包括gnuclibrarypopen()
,都使用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示例,标记和标记丢失处理程序是
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);
}
如果启动播放器,上面的循环检测到它没有播放