daemon 启动system V init 和 systemd 配置

先试着写一个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。

 

posted on 2017-02-23 12:46  沐浴凌风  阅读(294)  评论(0编辑  收藏  举报

导航