欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

daemon 启动system V init 和 systemd 配置

程序员文章站 2022-07-14 13:31:25
...

先试着写一个udpserver的daemon

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 4444
#define FDMAX 64
void initdaemon();
int main()
{
    #ifdef DAEMON
        initdaemon();
    #endif
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0)
        return -1;
    struct sockaddr_in server, client;
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(fd, (const struct sockaddr *)&server, sizeof(struct sockaddr));
    socklen_t len  = sizeof(struct sockaddr);
    char buff[2048] = {0};
    while(1)
    {
        recvfrom(fd, buff, 2048, 0,(struct sockaddr *)&client, &len);
       // printf("%s send msg: %s \n", inet_ntoa(client.sin_addr), buff);
        sendto(fd, "OK", 2, 0, (const struct sockaddr *)&client, len);
    }
}
void initdaemon()
{
    pid_t pid = fork(); //第一次fork 是因为创建会话的进程不能是进程组长,否则setsid会失败。
    if(pid > 0)
    {
        _exit(0);
    }
    else if(pid < 0)
    {
        _exit(1);
    }
    if(setsid() < 0)
        _exit(1);
    pid_t id = fork();//第二次fork是因为当前进程是会话组长 他有可能会获取控制终端,为了不让它获得控制终端,不让它成为组长。
    if(id > 0){
        _exit(0);
    }
    else if(id < 0)
    {
        _exit(1);
    }
    int i = 0;
    chdir("/");
    for(i; i < FDMAX; ++i)
        close(i);
    open("/dev/null", O_RDONLY);
    open("/dev/null", O_RDWR);
    open("/dev/null", O_RDWR); //将0 1 2重定向
}
gcc -DDAEMON udpserver.c -o udpserver添加宏DAEMON进行编译

下面是测试client端:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 4444
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("./udpclient IP\n");
        return 1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
        return 1;
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    if (!inet_pton(AF_INET, argv[1], (void *)&server.sin_addr))
    {
        printf("IP error\n");
        return -1;
    }
    char buff1[2048] ="sdfsdf";
    char buff2[2048] = {0};
    socklen_t size = sizeof(struct sockaddr);
    while(1)
    {
        sendto(fd, buff1, 2048, (const struct sockaddr *)&server, size);
        sleep(5);
        recvfrom(fd, buff2, 2048, 0, NULL, NULL);
        printf("recv : %s\n", buff2);
    }

}

先看一下system v init管理服务的一种写法:
我们可以看到在/etc/init.d 下面有很多脚本文件, 当系统启动时,init进程会根据不同的运行级别去执行不同的/etc/rcN.d 里面的脚本文件并会自动加上start参数,我们看一下sshd的例子:

set -e

# /etc/init.d/ssh: start and stop the OpenBSD "secure shell(tm)" daemon

test -x /usr/sbin/sshd || exit 0
( /usr/sbin/sshd -\? 2>&1 | grep -q OpenSSH ) 2>/dev/null || exit 0

umask 022

if test -f /etc/default/ssh; then
    . /etc/default/ssh
ficase "$1" in
  start)
    check_for_upstart 1
    check_privsep_dir
    check_for_no_start
    check_dev_null
    log_daemon_msg "Starting OpenBSD Secure Shell server" "sshd" || true
    if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/sshd.pid --exec /usr/sbin/sshd -- $SSHD_OPTS; then
        log_end_msg 0 || true
    else
        log_end_msg 1 || true
    fi
    ;;
  stop)
    check_for_upstart 0
    log_daemon_msg "Stopping OpenBSD Secure Shell server" "sshd" || true
    if start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/sshd.pid; then
        log_end_msg 0 || true
    else
        log_end_msg 1 || true
    fi
    ;;

  reload|force-reload)
    check_for_upstart 1
    check_for_no_start
    check_config
    log_daemon_msg "Reloading OpenBSD Secure Shell server's configuration" "sshd" || true
    if start-stop-daemon --stop --signal 1 --quiet --oknodo --pidfile /var/run/sshd.pid --exec /usr/sbin/sshd; then
        log_end_msg 0 || true
    else
        log_end_msg 1 || true
    fi
    ;;

  restart)
    check_for_upstart 1
    check_privsep_dir
    check_config
    log_daemon_msg "Restarting OpenBSD Secure Shell server" "sshd" || true
    start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile /var/run/sshd.pid
    check_for_no_start log_end_msg
    check_dev_null log_end_msg
    if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/sshd.pid --exec /usr/sbin/sshd -- $SSHD_OPTS; then
        log_end_msg 0 || true
    else
        log_end_msg 1 || true
    fi
    ;;

  try-restart)
    check_for_upstart 1
    check_privsep_dir
    check_config
    log_daemon_msg "Restarting OpenBSD Secure Shell server" "sshd" || true
    RET=0
    start-stop-daemon --stop --quiet --retry 30 --pidfile /var/run/sshd.pid || RET="$?"
    case $RET in
        0)
        # old daemon stopped
        check_for_no_start log_end_msg
        check_dev_null log_end_msg
        if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/sshd.pid --exec /usr/sbin/sshd -- $SSHD_OPTS; then
            log_end_msg 0 || true
        else
            log_end_msg 1 || true
        fi
        ;;
        1)
        # daemon not running
        log_progress_msg "(not running)" || true
        log_end_msg 0 || true
        ;;
        *)
        # failed to stop
        log_progress_msg "(failed to stop)" || true
        log_end_msg 1 || true
        ;;
    esac
    ;;

  status)
    check_for_upstart 1
    status_of_proc -p /var/run/sshd.pid /usr/sbin/sshd sshd && exit 0 || exit $?
    ;;

  *)
    log_action_msg "Usage: /etc/init.d/ssh {start|stop|reload|force-reload|restart|try-restart|status}" || true
    exit 1
esac

exit 0

写法也就是大致这个样子的利用shell 里的case 给不同参数运行不同的function 至于start-stop-daemon的用法可以查阅man page。
我们就可以将我们的udpserver 放到/usr/bin/目录下面,然后在/etc/init.d/目录下加上udp_server 脚本文件写法如下:

 #!/bin/bash
SERVER=/usr/bin/udpserver
if [ ! -e $SERVER ];then
    exit 1
fi
case "$1" in
    start)
        if [ -n "`pidof $SERVER`" ];then
            echo "Udpserver is running......"
        else
            $SERVER
        fi
    ;;

    stop)
        PID=`pidof /usr/bin/udpserver`
        [ -n "$PID" ] && kill -9 $PID
    ;;

    restart)
        PID=`pidof $SERVER`
        [ -n "$PID" ] && kill -9 $PID
        $SERVER
    ;;

    *)
        exit 1
    ;;

esac

再将你需要运行的等级目录下添加连接/etc /rc2.d/目录下K开头表示不启动服务,S打头表示启动服务 建立连接 ln -s ../init.d/udp_server S99udpserver , 表名开机启动 S后的数字字母等表示启动顺序。如此就可以实现system v init类型的启动服务。

若以systemd形式 将更为简单方便 此时就不需要udpserver 是一个daemon 可去掉宏进行编译。

在/etc/systemd/system/下建立文件udpserver.service 文件 内容大概为

[Unit]
Description=my dup server
After=network.target

[Service]
EnvironmentFile=
ExecStart=/usr/bin/udpserver
ExecReload=/bin/killall udpserver
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target

然后再执行 systemctl daemon-reload systemctl enable udpserver即可开机自启动。
systemctl start /stop/reload控制服务。journalctl可以查看启动日志。
总得来说还是systemd这种形式较为简单,它还有较好的日志系统,方便管理 不用将服务写为daemon。