如何在Linux上使用sudo后台运行dotnet应用程序?

12

我遇到了一些奇怪的问题,不确定该如何解决。我正在使用CodeDeploy将我的应用程序部署到一个AWS EC2实例中,在那里我需要指定脚本来执行实例上的进程,其中一个是.NET Core应用程序。

我在Windows上使用dotnet publish发布我的应用程序,并将所需的dll文件复制到EC2 Linux实例中。

我可以成功运行该应用程序,并且其表现符合预期。

sudo dotnet application.dll
然而,这只是在我终止应用程序之前并未在后台运行。
问题发生在我尝试在后台运行我的应用程序时,以便我可以继续运行其他应用程序或执行其他任务。
我通常使用screen或nohup来将应用程序运行在后台,但似乎对于这个应用程序不起作用。Screen在脚本中不起作用,而nohup运行应用程序,但会抛出一个错误。
为了使用nohup进行运行,我正在使用sudo nohup dotnet application.dll &命令,并在日志中收到错误信息。
Unhandled Exception: System.UnauthorizedAccessException: Access to the path is denied. ---> System.IO.IOException: Bad file descriptor
   --- End of inner exception stack trace ---
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
   at System.ConsolePal.Read(SafeFileHandle fd, Byte[] buffer, Int32 offset, Int32 count)
   at System.ConsolePal.UnixConsoleStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer()
   at System.IO.StreamReader.ReadLine()
   at System.IO.SyncTextReader.ReadLine()
   at System.Console.ReadLine()
   at Application.Program.Main(String[] args) in F:\Applications\Server\Program.cs:line 38

现在我明白为什么会出现这个错误,因为错误中的路径是Windows上我的项目路径,但为什么只有在使用nohup时才会发生这种情况呢?如果我在前台运行它,它就可以正常工作而不会抛出错误。

我如何在不使用screen的情况下在后台运行此应用程序?


编辑

我最初的想法是这可能是某种兼容性问题,因为它是在Windows上构建的,然而这并不是事实。我已经将项目移到Linux实例上并在那里重新发布了它,但当运行sudo nohup dotnet application.dll &时仍然收到相同的错误。

 Unhandled Exception: System.UnauthorizedAccessException: Access to the path is denied. ---> System.IO.IOException: Bad file descriptor
   --- End of inner exception stack trace ---
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
   at System.ConsolePal.Read(SafeFileHandle fd, Byte[] buffer, Int32 offset, Int32 count)
   at System.ConsolePal.UnixConsoleStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer()
   at System.IO.StreamReader.ReadLine()
   at System.IO.SyncTextReader.ReadLine()
   at System.Console.ReadLine()
   at Application.Program.Main(String[] args) in /opt/servers/Server/Program.cs:line 38

我刚刚尝试了:

sudo dotnet application.dll > out.log 2>&1 &

这似乎可以无错误地运行程序, 但是当执行另一个命令时, 后台应用程序会被发到一个 STOPPED 状态并停止运行.

[1]+ Stopped sudo dotnet application.dll > out.log 2>&1

ubuntu@:/opt/servers/Server$ sudo dotnet application.dll > out.log  2>&1 &
[1] 2448
ubuntu@:/opt/servers/Server$ netstat -tulpn
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
udp   214656      0 0.0.0.0:1000            0.0.0.0:*                           -
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -

[1]+  Stopped                 sudo dotnet application.dll > out.log 2>&1

下一步是研究在分离模式下使用screen

screen -d -m bash -c 'cd /opt/server && sudo dotnet application.dll &'

这个命令目前不会生成一个 screen 窗口...

编辑

我必须删除末尾的&,现在它按预期工作了。

screen -d -m -S SERVER bash -c 'cd /opt/server && sudo dotnet application.dll'


4个回答

12

如果是在Windows上(我不确定您是否需要这个),只需运行 start 命令即可实现异步运行。以下是示例(前台和后台)

start dotnet yourdll.dll

start /b dotnet yourdll.dll

在Linux上

nohup ./yourSelfContainedapp > /dev/null 2>&1 &

或者

nohup dotnet yourapp.dll > /dev/null 2>&1 &

还有,您可以使用以上的屏幕命令(可以进行分离/附加回来)


1
仅运行'nohup dotnet myapp.dll'即可正常工作,无需'> /dev/null 2>&1 &'。不确定这是否是一个好主意,哈哈...您能解释一下'> /dev/null 2>&1 &'部分的作用吗?----编辑:算了,我刚刚找到了一些资料,它基本上会丢弃所有控制台输出。我所做的是将输出记录在某个地方。 - mbuchok
“> /dev/null 2>&1 &” 看起来有些自我打击。据我所知,这个问题是由于无法连接记录器引起的... - Auspex

8
对我来说,问题是:
Console.ReadKey();

我已经用以下代码进行了替换:
var cancellationTokenSource = new CancellationTokenSource();
AppDomain.CurrentDomain.ProcessExit += (s, e) => cancellationTokenSource.Cancel();
Console.CancelKeyPress += (s, e) => cancellationTokenSource.Cancel();
await Task.Delay(-1, cancellationTokenSource.Token).ContinueWith(t => { });

使用此代码,您可以通过CTRL+C或SIGTERM信号关闭控制台应用程序。

希望这有所帮助!


1
之前的解决方案对我没有用,消息监听器无法接收消息,所以我在主程序的最后一行将Console.ReadLine()替换为Thread.Sleep(Timeout.Infinite)。这解决了nohup问题、日志问题和后台运行问题...我不记得在Java中需要做这些事情来保持Tibco或Rabbit消息监听器的运行。

0
更好的解决方案是在尝试读取输入流之前测试其是否可用。
在Linux上,您可以执行以下操作:
bool isInteractive = true;
try
{
    Console.In.Peek();
}
catch (UnauthorizedAccessException)
{
    // UnauthorizedAccessException: Access to the path is denied. ---> System.IO.IOException: Bad file descriptor
    // repro: `nohup dotnet myapp.dll &` (no input stream)
    isInteractive = false;
}

然后,您可以将所有对 Console.In 的调用包装在条件语句中,例如:

if (inInteractive) {
    Console.In.ReadLine();
}

在Windows上,您可能需要尝试不同的选项以查看哪个有效,但逻辑类似:如果没有可用的控制台输入流,请勿尝试从中读取。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接