리눅스에서 데몬 생성
Linux 에서는 정지할 수 없는 데몬과 파일 시스템 변경을 감시하는 데몬을 추가합니다.변경이 검출되었을 경우는, 기동한 콘솔의 패스와 새로운 행이 기입됩니다.
파일 시스템의 코드 변경은 이미 거의 완료되어 있습니다만, 데몬을 작성하는 방법을 알 수 없습니다.
제 코드는 http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html 에서 구할 수 있습니다.
포크 다음에 어떻게 해야 하나요?
int main (int argc, char **argv) {
pid_t pID = fork();
if (pID == 0) { // child
// Code only executed by child process
sIdentifier = "Child Process: ";
}
else if (pID < 0) {
cerr << "Failed to fork" << endl;
exit(1);
// Throw exception
}
else // parent
{
// Code only executed by parent process
sIdentifier = "Parent Process:";
}
return 0;
}
Linux 에서는 정지할 수 없는 데몬과 파일 시스템 변경을 감시하는 데몬을 추가합니다.변경이 검출되었을 경우는, 기동한 콘솔의 패스와 새로운 행이 기입됩니다.
데몬은 백그라운드에서 동작하며 (보통...) TTY에 속하지 않기 때문에 stdout/stderr를 원하는 방식으로 사용할 수 없습니다.보통 syslog 데몬(syslogd)은 메시지를 파일에 로깅하기 위해 사용됩니다(debug, error, ...).
이 외에도 프로세스를 데몬화하기 위해 필요한 몇 가지 단계가 있습니다.
올바르게 기억하고 있는 경우는, 다음의 순서가 있습니다.
- 포킹이 성공하면 부모 프로세스를 분리하여 종료합니다.-> 부모 프로세스가 종료되었기 때문에 자녀 프로세스는 백그라운드에서 실행됩니다.
- setsid - 새 세션을 만듭니다.호출 프로세스는 새 세션의 리더가 되고 새 프로세스 그룹의 프로세스 그룹 리더가 됩니다.이것으로 프로세스가 제어단말기(CTTY)에서 분리되었습니다.
- 신호 포착 - 신호 무시 및/또는 처리
- 다시 fork하여 부모 프로세스를 종료하고 세션 주도 프로세스를 삭제하도록 합니다(세션 리더만 TTY를 다시 받을 수 있습니다).
- chdir - 데몬의 작업 디렉토리를 변경합니다.
- umask - 파일모드 마스크를 데몬의 필요에 따라 변경합니다.
- close - 상위 프로세스에서 상속될 수 있는 열려 있는 모든 파일 설명자를 닫습니다.
시작점을 제공하려면:기본 단계를 보여주는 이 스켈레톤 코드를 보세요.이 코드는 GitHub에서도 분기할 수 있습니다: Linux 데몬의 기본 스켈레톤
/*
* daemonize.c
* This example daemonizes a process, writes a few log messages,
* sleeps 20 seconds and terminates afterwards.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
static void skeleton_daemon()
{
pid_t pid;
/* Fork off the parent process */
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* On success: The child process becomes session leader */
if (setsid() < 0)
exit(EXIT_FAILURE);
/* Catch, ignore and handle signals */
//TODO: Implement a working signal handler */
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* Fork off for the second time*/
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* Set new file permissions */
umask(0);
/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir("/");
/* Close all open file descriptors */
int x;
for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
{
close (x);
}
/* Open the log file */
openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
skeleton_daemon();
while (1)
{
//TODO: Insert daemon code here.
syslog (LOG_NOTICE, "First daemon started.");
sleep (20);
break;
}
syslog (LOG_NOTICE, "First daemon terminated.");
closelog();
return EXIT_SUCCESS;
}
- 컴파일합니다.
gcc -o firstdaemon daemonize.c
- 합니다.
./firstdaemon
제대로 합니다.
ps -xj | grep firstdaemon
출력은 다음과 같습니다.
+------+------+------+------+-----+-------+------+------+------+-----+| PPID | PID | PGID | SID | TTY | TPGID | STAT | UID | TIME | CMD |+------+------+------+------+-----+-------+------+------+------+-----+| 1 | 3387 | 3386 | 3386 | ? | - 1 | S | 1000 | 0:00 | ./ |+------+------+------+------+-----+-------+------+------+------+-----+
여기서 확인할 수 있는 것은 다음과 같습니다.
- 데몬에 제어 단자가 없습니다(TTY = ?).
- Parent Process ID(PPID; 부모 프로세스 ID)는 1(init 프로세스)입니다.
- 프로세스가 세션 리더가 아님을 의미하는 PID != SID
(두 fork - PID!= SID로 인해 프로세스가 TTY를 다시 제어할 수 없습니다.
syslog 읽기:
- 파일syslog 파일을 .는 여기 있습니다.
/var/log/syslog
하다, 하다, 하다, 하다.
grep firstdaemon /var/log/syslog
출력은 다음과 같습니다.
firstdaemon [3387] :첫 번째 데몬이 시작되었습니다. firstdaemon [3387] :첫 번째 데몬이 종료되었습니다.
참고: 실제로는 신호 핸들러를 구현하고 로깅을 적절하게 설정할 수도 있습니다(파일, 로그 수준...).
추가 정보:
man 7 daemon
에 데몬 작성 방법에 대해 자세히 설명합니다.제 답변은 이 매뉴얼에서 발췌한 것입니다.
데몬에는 적어도 두 가지 유형이 있습니다.
- 기존 SysV 데몬(구식),
- systemdaemons(새로운 스타일)
SysV 대몬스
기존의 SysV 데몬에 관심이 있는 경우는, 다음의 순서를 실장할 필요가 있습니다.
- 표준 입력, 출력 및 오류(첫 번째 세 개의 파일 기술자 0, 1, 2)를 제외한 열려 있는 모든 파일 기술자를 닫습니다.이렇게 하면 실수로 전달된 파일 기술자가 데몬 프로세스에 남아 있지 않습니다.Linux 에서는, 다음의 순서로 반복하는 것이 최적인 실장입니다.
/proc/self/fd
파일 디스크립터 3에서가 반환한 값으로의 폴백을 실시합니다.RLIMIT_NOFILE
.- 모든 신호 핸들러를 기본값으로 리셋합니다.이것은, 사용 가능한 신호를 사용해 다음의 제한까지 반복하는 것에 의해서 최적이 됩니다.
_NSIG
을 「」으로 합니다.SIG_DFL
.- 를 사용하여 신호 마스크를 리셋합니다.
- 데몬 런타임에 부정적인 영향을 줄 수 있는 환경 변수를 제거하거나 재설정하여 환경 블록을 삭제합니다.
- 콜 fork()배경 과정을 만들 수 있습니다.
- 그 아이에서는 어느 터미널에서 독자적인 세션을 창조하는 setsid().
- 그 아이에서, 역시, 데몬이 다시 터미널은 re-acquire지 않을 수 있도록 하는 fork().
- 그래서 두번째 아이(실제daemon 과정)이 유지되는 첫번째 아이에, exit()를 호출합니다.이 모든과는해야 하는 것은 가장daemon 과정init/PID 1에 re-parented 것을 보장한다.
- 그daemon 과정에서 표준 입력, 출력 및 오류로 /dev/null 연결합니다.
- 파일 모드, mkdir()등등 open()에 전달된 직접적으로 만들어진 파일과 디렉터리의 액세스 모드를 제어하는daemon 과정에서 0으로, umask를 리셋 한다.
- 그daemon 과정에서 루트 디렉터리를(현재 디렉터리에 따라 변한다.
/
순서대로 분리되었거나 존재하지 않는 것이 무의식적으로 블록을 벌이고 있는 데몬 지점이 발생하지 않도록.- 그daemon 과정에 PID파일에, 예를 들어 데몬이 PID(로getpid(반환))를 쓴다.
/run/foobar.pid
(가상적인 데몬을"foobar")은 데몬이 한번 이상 시동이 걸리지 않을 수 있도록 하는.때 같은 시간은 PID이전에 PID파일에 저장되어 더 이상 또는 해외 과정에 속한다는 것을 존재하는에서 확인될 것이고 PID파일에서만 업데이트됩니다 이race-free 패션에서 구현되어야 합니다.- 만약 및 적용할 수 있는 가능한daemon 과정에서, 특권을 떨어뜨린다.
- 그daemon 과정부터 원래 과정 그 초기화 완전한 시작 알려 주셨음 합니다.이는 첫번째 fork()기 전에 있고 이를daemon 과정 원서로 사용할 수가 만들어집니다 익명 파이프 또는 유사한 통신 채널을 통해 구현할 수 있다.
- 원래 과정에서 exit()를 호출합니다.데몬을 호출한 프로세스는 초기화가 완료되고 모든 외부 통신 채널이 확립되어 접근 가능한 후에 이러한 상황이 발생하는 것을 신뢰할 수 있어야 합니다.
다음 경고에 주의해 주십시오.
BSD 함수는 이들 스텝의 서브셋만 구현되므로 사용하지 마십시오.
SysV 시스템과의 호환성을 제공할 필요가 있는 데몬은 위에서 설명한 방식을 구현해야 합니다.단, 디버깅을 용이하게 하고 systemd를 사용하여 시스템 통합을 단순화하기 위해 명령줄 인수를 사용하여 이 동작을 옵션으로 설정하고 설정할 것을 권장합니다.
는 POSIX에 준거하고 있지 않습니다.
신식 대몬
새로운 스타일의 데몬의 경우 다음 단계를 수행하는 것이 좋습니다.
- if
SIGTERM
수신되면 데몬을 셧다운하고 깔끔하게 종료합니다.- if
SIGHUP
설정하다- 메인 데몬 프로세스에서 올바른 종료 코드를 입력합니다.이는 서비스 오류 및 문제를 검출하기 위해 init 시스템에 의해 사용됩니다.SysV init 스크립트의 LSB 권장 사항에 따라 종료 코드 방식을 따르는 것이 좋습니다.
- 가능하면 D-Bus IPC 시스템을 통해 데몬의 제어 인터페이스를 노출하고 초기화 마지막 단계로 버스 이름을 가져옵니다.
- systemd와의 통합을 위해 데몬의 시작, 정지 및 기타 유지보수에 대한 정보를 포함하는 .service 유닛파일을 제공합니다.자세한 것은, 을 참조해 주세요.
- 가능한 한 init 시스템의 기능에 의존하여 파일, 서비스 및 기타 자원에 대한 데몬 접근을 제한합니다.즉, systemd의 경우, 자신의 것을 실장하는 대신 systemd의 자원 제한 제어에 의존하거나, 데몬에 실장하는 대신 systemd의 특권 폐기 코드에 의존합니다.사용 가능한 컨트롤에 대해서는, 을 참조해 주세요.
- D-Bus를 사용하는 경우 D-Bus 서비스 액티베이션컨피규레이션파일을 제공하여 데몬 버스를 활성화 합니다.이 방법에는 여러 가지 장점이 있습니다.데몬을 온 디맨드로 느릿느릿하게 기동할 수 있습니다.또한 데몬을 필요로 하는 다른 데몬과 병행하여 기동할 수도 있습니다.이것에 의해, 버스 요구가 없어지지 않고, 액티브한 서비스를 요구하기 위해서, 장해가 발생해도 데몬을 재기동할 수 있습니다.상세한 것에 대하여는, 이하를 참조해 주세요.
- 데몬이 소켓을 통해 다른 로컬프로세스 또는 리모트클라이언트에 서비스를 제공하는 경우 다음 스킴에 따라 데몬을 소켓 활성화해야 합니다.D-Bus 액티베이션과 마찬가지로 온 디맨드로 서비스를 시작할 수 있을 뿐만 아니라 서비스 시작의 병렬화를 개선할 수 있습니다.또, 스테이트리스 프로토콜(syslog, DNS 등)의 경우, 소켓 베이스의 액티베이션 실장 데몬을 1개의 요구도 잃지 않고 재기동할 수 있습니다.상세한 것에 대하여는, 이하를 참조해 주세요.
- 해당하는 경우 데몬은 인터페이스를 통해 시작 완료 또는 상태 업데이트에 대해 init 시스템에 통지해야 합니다.
- 새로운 스타일의 데몬은 콜을 사용하여 시스템 syslog 서비스에 직접 로그하는 대신 를 통해 표준 에러에 단순히 로그하는 것을 선택할 수 있습니다.이 에러는 init 시스템에 의해 syslog로 전송됩니다.로그 레벨이 필요한 경우 Linux 커널의 레벨시스템과 같은 스타일에 따라 각 로그 행에 "<4>"와 같은 문자열(syslog priority scheme의 로그레벨 4 "WARNING"의 경우)을 부가하여 부호화할 수 있습니다.상세한 것에 대하여는,및 을 참조해 주세요.
자세한 내용은 전체 내용을 참조하십시오.
데몬 템플릿
새로운 스타일의 데몬 링크에 이어 데몬 템플릿을 작성했습니다.
템플릿 코드 전체를 GitHub에서 찾을 수 있습니다.여기서
Main.cpp
// This function will be called when the daemon receive a SIGHUP signal.
void reload() {
LOG_INFO("Reload function called.");
}
int main(int argc, char **argv) {
// The Daemon class is a singleton to avoid be instantiate more than once
Daemon& daemon = Daemon::instance();
// Set the reload function to be called in case of receiving a SIGHUP signal
daemon.setReloadFunction(reload);
// Daemon main loop
int count = 0;
while(daemon.IsRunning()) {
LOG_DEBUG("Count: ", count++);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
LOG_INFO("The daemon process ended gracefully.");
}
Daemon.hpp
class Daemon {
public:
static Daemon& instance() {
static Daemon instance;
return instance;
}
void setReloadFunction(std::function<void()> func);
bool IsRunning();
private:
std::function<void()> m_reloadFunc;
bool m_isRunning;
bool m_reload;
Daemon();
Daemon(Daemon const&) = delete;
void operator=(Daemon const&) = delete;
void Reload();
static void signalHandler(int signal);
};
Daemon.cpp
Daemon::Daemon() {
m_isRunning = true;
m_reload = false;
signal(SIGINT, Daemon::signalHandler);
signal(SIGTERM, Daemon::signalHandler);
signal(SIGHUP, Daemon::signalHandler);
}
void Daemon::setReloadFunction(std::function<void()> func) {
m_reloadFunc = func;
}
bool Daemon::IsRunning() {
if (m_reload) {
m_reload = false;
m_reloadFunc();
}
return m_isRunning;
}
void Daemon::signalHandler(int signal) {
LOG_INFO("Interrup signal number [", signal,"] recived.");
switch(signal) {
case SIGINT:
case SIGTERM: {
Daemon::instance().m_isRunning = false;
break;
}
case SIGHUP: {
Daemon::instance().m_reload = true;
break;
}
}
}
daemon-interface(데몬 교환)서비스
[Unit]
Description=Simple daemon template
After=network.taget
[Service]
Type=simple
ExecStart=/usr/bin/daemon-template --conf_file /etc/daemon-template/daemon-tenplate.conf
ExecReload=/bin/kill -HUP $MAINPID
User=root
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=daemon-template
[Install]
WantedBy=multi-user.target
리눅스에서는 중지할 수 없는 프로세스를 생성할 수 없습니다.루트 사용자(uid=0)는 프로세스에 신호를 보낼 수 있으며, 포착할 수 없는 신호는 SIGKILL=9와 SIGSTOP=19 두 가지가 있습니다.또한 다른 신호(잡히지 않은 경우)도 프로세스가 종료될 수 있습니다.
프로그램/데몬의 이름과 프로그램 실행 경로(아마 "/" 또는 "/tmp")를 지정할 수 있는 보다 일반적인 데몬화 함수를 원할 수 있습니다.stderr 및 stdout(및 stdin을 사용한 제어 경로)에 대한 파일을 제공할 수도 있습니다.
필요한 것은 다음과 같습니다.
#include <stdio.h> //printf(3)
#include <stdlib.h> //exit(3)
#include <unistd.h> //fork(3), chdir(3), sysconf(3)
#include <signal.h> //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h> //syslog(3), openlog(3), closelog(3)
그리고 여기 좀 더 일반적인 기능이 있습니다.
int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
if(!path) { path="/"; }
if(!name) { name="medaemon"; }
if(!infile) { infile="/dev/null"; }
if(!outfile) { outfile="/dev/null"; }
if(!errfile) { errfile="/dev/null"; }
//printf("%s %s %s %s\n",name,path,outfile,infile);
pid_t child;
//fork, detach from process group leader
if( (child=fork())<0 ) { //failed fork
fprintf(stderr,"error: failed fork\n");
exit(EXIT_FAILURE);
}
if (child>0) { //parent
exit(EXIT_SUCCESS);
}
if( setsid()<0 ) { //failed to become session leader
fprintf(stderr,"error: failed setsid\n");
exit(EXIT_FAILURE);
}
//catch/ignore signals
signal(SIGCHLD,SIG_IGN);
signal(SIGHUP,SIG_IGN);
//fork second time
if ( (child=fork())<0) { //failed fork
fprintf(stderr,"error: failed fork\n");
exit(EXIT_FAILURE);
}
if( child>0 ) { //parent
exit(EXIT_SUCCESS);
}
//new file permissions
umask(0);
//change to path directory
chdir(path);
//Close all open file descriptors
int fd;
for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
{
close(fd);
}
//reopen stdin, stdout, stderr
stdin=fopen(infile,"r"); //fd=0
stdout=fopen(outfile,"w+"); //fd=1
stderr=fopen(errfile,"w+"); //fd=2
//open syslog
openlog(name,LOG_PID,LOG_DAEMON);
return(0);
}
여기 데몬이 되어 주변을 맴돌다가 떠나는 샘플 프로그램이 있습니다.
int
main()
{
int res;
int ttl=120;
int delay=5;
if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
fprintf(stderr,"error: daemonize failed\n");
exit(EXIT_FAILURE);
}
while( ttl>0 ) {
//daemon code here
syslog(LOG_NOTICE,"daemon ttl %d",ttl);
sleep(delay);
ttl-=delay;
}
syslog(LOG_NOTICE,"daemon ttl expired");
closelog();
return(EXIT_SUCCESS);
}
SIG_IGN은 신호를 포착하여 무시하도록 지시합니다.신호 수신을 로그에 기록하고 플래그(그레이스 풀 셧다운을 나타내는 플래그 등)를 설정할 수 있는 신호 핸들러를 구축할 수 있습니다.
도 한번 .daemon
★★★★
#include <unistd.h>
int daemon(int nochdir, int noclose);
man 페이지부터:
daemon() 함수는 제어 단말기에서 분리하여 백그라운드에서 시스템 데몬으로 실행하기를 원하는 프로그램을 위한 함수입니다.
nochdir가 0인 경우 daemon()은 호출 프로세스의 현재 작업 디렉토리를 루트 디렉토리("/")로 변경합니다.그렇지 않은 경우 현재 작업 디렉토리는 변경되지 않습니다.
만약noclose 0이다, daemon()리디렉션 표준 입력, 표준 출력과 표준 오차 /dev/null려면이고, 그렇지 않으면 아무 변화가 없는 이러한 파일 설명자에 만들어진다.
만약 당신의 앱은 하나의:.
{
".sh": "bash",
".py": "python",
".rb": "ruby",
".coffee" : "coffee",
".php": "php",
".pl" : "perl",
".js" : "node"
}
그리고 너는 노드 별로 상관하지 않는다.JS의존한 다음고 다음 NodeJS을 설치한다.
npm install -g pm2
pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above
pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores
pm2 list
모든 앱 재부팅(그리고 pm2 daemonise)에서 실행:을 지키기 위해서.
pm2 startup
pm2 save
이제 할 수 있습니다.
service pm2 stop|restart|start|status
(또한 쉽게 할 코드가 변하는 과정 당신의 app 디렉터리와 자동차의 코드 변화로 재시작 앱 과정을 볼 수 있).
fork()를 호출하여 당신은 자식 프로세스가 만들어집니다.포크 성공적이다(포크가 0이 아니PID 돌아왔다.)실행 이 점에서 자식 프로세스 내에서 계속될 것이다.이 경우에 우리는 품위 있게 그리고 아이를 과정에서 우리의 일을 계속하는 것이 부모 프로세스를 종료하고 싶다.
아마도 이:http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html도움이 될 것이다.
배경의 데몬은 단지 과정이다.때 OS부츠, linux에/etc/rc.d/rc.local( 뒤쫓다 다른 모든 스크립트)또는 /etc/startup에다가 명령을 추가하는 당신의 프로그램을 시작하고 싶다.쉐
창문에서 서비스를 등록해서 자동으로 행정부 ->의, 서비스 위원회에서 시작하는 것을 두고 있는 서비스를 만든다.
나는 첫째로 요구"는 멈추지 않을 수 있는 데몬..."에서 멈출 수 있다.
친구여, 불가능하오. 하지만 당신은 훨씬 더 좋은 도구인 커널 모듈로 같은 것을 얻을 수 있습니다.
http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring
모든 데몬은 멈출 수 있다.어떤 것은 다른 것보다 더 쉽게 멈출 수 있다.파트너가 홀드다운되어 있는 데몬 쌍이라도 손실 시 파트너를 재기동하는 것을 중지할 수 있습니다.조금만 더 열심히 하면 돼.
언급URL : https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux
'programing' 카테고리의 다른 글
C에서 프리프로세서 변수를 인쇄할 수 있습니까? (0) | 2022.06.13 |
---|---|
Vue.js 컴포넌트 내의 모멘트 라이브러리를 사용하는 방법 (0) | 2022.06.13 |
STDIN_FILENO와 STDIN_FILENO의 차이점은 무엇입니까? (0) | 2022.06.13 |
Java 기본 생성자 (0) | 2022.06.13 |
이 Nuxt-Socket-io 이미터는 왜 그 액션을 트리거하지 않는가? (0) | 2022.06.13 |