Raspberry Pi(Stretch)를 재부팅하면 데몬이 /run/user/1000
존재하지 않기 때문에 시작되지 않습니다. 이것은 내 유닛 파일입니다:
[Unit]
Description=SBFspot Upload Daemon
[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL
[Install]
WantedBy=default.target
몇 번 다시 시도한 후에 제대로 작동 하도록 구성했지만 Restart=on-failure
실제로는 내가 원하는 것이 아닙니다. 데몬이 /run/user/1000
설치를 기다리기를 원합니다 . 시도했지만 After=run-user-1000.mount
여전히 실패했습니다.
이것이 가능합니까, 아니면 고집해야합니까 Restart=on-failure
?
답변1
/run/user/1000
물론 사용자 #1000이 로그인하거나 명시적으로 xyr 사용자별 서비스 관리를 시작하기 전까지는 존재하지 않습니다. 이를 사용하기 위한 전체 메커니즘이 존재해서는 안 됩니다.
이 프로그램의 버그 #215당신이 생각하는 것보다 훨씬 더 깊습니다. 이 서비스 단위 파일은 매우 잘못되었습니다.프로그램 자체의 동작도 마찬가지이다.. 시스템화된 서비스 단위의 기본을 실제로 이해하지 못하는 데 기반을 둔 화물 매니아 프로그래밍이 많이 있습니다.
- 서비스 단위는 쉘 스크립트가 아닙니다. 시스템 매뉴얼하다설명하다. 여기에서 설정하면 서비스 프로그램이 일부 추가 매개변수 와
ExecStart
함께 실행됩니다 .2>&1>
/dev/null
- 서비스 관리자는 하나의 서비스만 실행 중인지 확인했습니다. 여기에 추가된 코드는 모두 불필요한 쓰레기입니다.
- 불안정하고 위험한 PID 파일 메커니즘은아니요사용. 적절한 서비스 관리에는 자리가 없습니다.
- 서비스 관리자는 데몬 컨텍스트에서 서비스 호출도 처리합니다. 다른 많은 코드
main()
는 다음과 같습니다.반품악마화 오류에 근거한 불필요한 쓰레기.- 프로그램이
fork()
전혀 실행되어서는 안 되며 서비스 준비 메커니즘이 지정되어서도 안 됩니다Type=forking
. 현실 세계의 많은 프로그램과 마찬가지로 이 프로그램도아니요먼저 포크 준비 프로토콜에 대해 이야기해 보겠습니다. - 계획은이미슈퍼유저로 실행합니다.
User=root
불필요하며 서비스를 실제로 재설계해야 합니다.아니요슈퍼유저 권한으로 실행해야 하지만 대신 권한이 없는 전용 서비스 계정의 후원으로 실행됩니다. - 서비스 관리자는이미표준 출력과 오류를 기록하고 이 프로그램보다 더 나은 작업을 수행합니다. 자체 개발한 이 로깅 시스템은 전체 파일 시스템을 채울 때까지만 로그 파일을 증가시키며, 수퍼유저용으로 예약된 모든 비상 공간을 소비합니다.
- 귀하의 로그는 표준 오류일 뿐이며 C++에서
std::clog
. - 실제로,
fork()
표준 오류로 리디렉션되는 모든 코드사용해서는 안됩니다. 서비스 관리 핸들모두세션 리더에서 작업 디렉터리로, umask에서 표준 I/O로 이동하며 올바르게 수행됩니다. 이 앱은 그렇게 하지 않으며, 그렇게 시도해서도 안 됩니다.어느서비스 관리자에서 사용할 때의 모습입니다.Boost에서 얻은 모든 것이 잘못되었습니다.
- 프로그램이
- 세 가지 서비스 단위는 불필요한 유지 관리 비용입니다. 설정 만
After
다르며 하나로 병합할 수 있습니다. - 무례한 해고는 성공이 아닙니다. 있다는 점을 고려하면이미종료 시 파일 정리와 관련된 문제
SuccessExitStatus=SIGKILL
는 버그입니다. 정상적인 종료는 정상적으로 전달되어야SIGTERM
하며SIGKILL
예외적인 것으로 간주되어야 합니다. (물론output
이미 설명했듯이 전체 파일 메커니즘은 제대로 구현되지 않은 로컬 로깅 메커니즘이므로 서비스 관리에서 사용해서는 안됩니다.) 이것이 systemd의 기본 설정입니다. - 데이터베이스 개체 및 기타 콘텐츠에 대한 소멸자를 실행해야 합니다.
main()
떠나지 마세요exit()
.
올바르게 구현되고 서비스 관리자(daemontools, runit, s6, nosh, systemd 등)에서 실행되는 데몬은 훨씬 더 짧습니다.
…//지금까지 마찬가지 무효 pvo_upload(무효) { std::clog << "데몬 프로세스 시작..." << std::endl; 공공 서비스 코드(); std::clog << "데몬 중지..." << std::endl; } int main(int argc, char *argv[]) { 정수 c; const char *config_file = ""; /* 명령줄 구문 분석*/ 동시에(1) { 정적 구조 옵션 long_options[] = { {"구성 파일",required_argument,0,'c'}, { 0, 0, 0, 0 } }; int 옵션 인덱스 = 0; c = getopt_long(argc, argv, "c:", long_options, &option_index); if (c == -1) 인터럽트; 스위치 (c) { 사례 "c": 구성 파일=optarg; 나머지; 기본: EXIT_FAILURE를 반환합니다. 나머지; } } if (cfg.readSettings(argv[0], config_file) != 구성::CFG_OK) EXIT_FAILURE를 반환합니다. std::clog << "SBFspotUploadDaemon 버전 시작 중" << 버전 << std::endl; // 데이터베이스에 접근 가능한지 확인 db_SQL_Base db = db_SQL_Base(); db.open(cfg.getSqlHostname(), cfg.getSqlUsername(), cfg.getSqlPassword(), cfg.getSqlDatabase()); if (!db.isopen()) { std::clog << "데이터베이스를 열 수 없습니다. 구성을 확인하십시오." << std::endl; EXIT_FAILURE를 반환합니다. } // 데이터베이스 버전 확인 int 스키마_버전 = 0; db.get_config(SQL_SCHEMAVERSION, 스키마_버전); db.close(); if (schema_version < SQL_MINIMUM_SCHEMA_VERSION) { std::clog << "데이터베이스를 버전으로 업그레이드" << SQL_MINIMUM_SCHEMA_VERSION << std::endl; EXIT_FAILURE를 반환합니다. } //신호 처리기를 설치합니다. // 서비스 관리자가 보낸 서비스 중지 신호에 응답합니다. signal(SIGTERM, 핸들러); // 데몬 루프 시작 pvo_upload(); return 종료_성공; }
그리고 서비스 단위도 더 짧습니다.
[단위] 설명=SBFspot 업로드 데몬 이후=mysql.service mariadb.service network.target [제공하다] 유형=단순 시간 초과 중지 초=10 ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon 다시 시작=성공 [설치하다] WantedBy=다중 사용자.대상
systemctl status
로그 출력을 사용하고 볼 수 있습니다 journalctl
( -u
필요한 경우 옵션 및 서비스 이름 포함).
추가 읽기
- 조나단 데보인 폴라드(2016).금세기에는 또는 를 사용하지 마십시오
logrotate
.newsyslog
. 자주 주어지는 답변입니다. - 조나단 드보인 폴라드(2001). Unix 데몬을 설계할 때 피해야 할 실수. 자주 주어지는 답변입니다.
- 조나단 데보인 폴라드(2015).Unix 데몬의 준비 프로토콜 문제. 일반적인 답변.
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132