Deep Analysis of the Reasons for the "The server quit without updating PID file" Error in MySQL Startup Times

Many children's shoes encountered this mistake when they started mysql.

First, clarify that the premise of this error is to start mysql through a service script. Starting a mysql instance with mysqld_safe or mysqld does not report this error.

So what exactly is the reason for this mistake?

Haha, care less children's shoes can jump directly to the conclusion of the article.

summary

 

Next, analyze mysql's service startup script

The script is complete as follows:

#!/bin/sh
# Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
# This file is public domain and comes with NO WARRANTY of any kind

# MySQL daemon start/stop script.

# Usually this is put in /etc/init.d (at least on machines SYSV R4 based
# systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql.
# When this is done the mysql server will be started when the machine is
# started and shut down when the systems goes down.

# Comments to support chkconfig on RedHat Linux
# chkconfig: 2345 64 36
# description: A very fast and reliable SQL database engine.

# Comments to support LSB init script conventions
### BEGIN INIT INFO
# Provides: mysql
# Required-Start: $local_fs $network $remote_fs
# Should-Start: ypbind nscd ldap ntpd xntpd
# Required-Stop: $local_fs $network $remote_fs
# Default-Start:  2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop MySQL
# Description: MySQL is a very fast and reliable SQL database engine.
### END INIT INFO
 
# If you install MySQL on some other places than /usr/local/mysql, then you
# have to do one of the following things for this script to work:
#
# - Run this script from within the MySQL installation directory
# - Create a /etc/my.cnf file with the following information:
#   [mysqld]
#   basedir=<path-to-mysql-installation-directory>
# - Add the above to any other configuration file (for example ~/.my.ini)
#   and copy my_print_defaults to /usr/bin
# - Add the path to the mysql-installation-directory to the basedir variable
#   below.
#
# If you want to affect other MySQL variables, you should make your changes
# in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files.

# If you change base dir, you must also change datadir. These may get
# overwritten by settings in the MySQL configuration files.

basedir=
datadir=

# Default value, in seconds, afterwhich the script should timeout waiting
# for server start. 
# Value here is overriden by value in my.cnf. 
# 0 means don't wait at all
# Negative numbers mean to wait indefinitely
service_startup_timeout=900

# Lock directory for RedHat / SuSE.
lockdir='/var/lock/subsys'
lock_file_path="$lockdir/mysql"

# The following variables are only set for letting mysql.server find things.

# Set some defaults
mysqld_pid_file_path=
if test -z "$basedir"
then
  basedir=/usr/local/mysql
  bindir=/usr/local/mysql/bin
  if test -z "$datadir"
  then
    datadir=/usr/local/mysql/data
  fi
  sbindir=/usr/local/mysql/bin
  libexecdir=/usr/local/mysql/bin
else
  bindir="$basedir/bin"
  if test -z "$datadir"
  then
    datadir="$basedir/data"
  fi
  sbindir="$basedir/sbin"
  libexecdir="$basedir/libexec"
fi

# datadir_set is used to determine if datadir was set (and so should be
# *not* set inside of the --basedir= handler.)
datadir_set=

#
# Use LSB init script functions for printing messages, if possible
#
lsb_functions="/lib/lsb/init-functions"
if test -f $lsb_functions ; then
  . $lsb_functions
else
  log_success_msg()
  {
    echo " SUCCESS! $@"
  }
  log_failure_msg()
  {
    echo " ERROR! $@"
  }
fi

PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin"
export PATH

mode=$1    # start or stop

[ $# -ge 1 ] && shift


other_args="$*"   # uncommon, but needed when called from an RPM upgrade action
           # Expected: "--skip-networking --skip-grant-tables"
           # They are not checked here, intentionally, as it is the resposibility
           # of the "spec" file author to give correct arguments only.

case `echo "testing\c"`,`echo -n testing` in
    *c*,-n*) echo_n=   echo_c=     ;;
    *c*,*)   echo_n=-n echo_c=     ;;
    *)       echo_n=   echo_c='\c' ;;
esac

parse_server_arguments() {
  for arg do
    case "$arg" in
      --basedir=*)  basedir=`echo "$arg" | sed -e 's/^[^=]*=//'`
                    bindir="$basedir/bin"
            if test -z "$datadir_set"; then
              datadir="$basedir/data"
            fi
            sbindir="$basedir/sbin"
            libexecdir="$basedir/libexec"
        ;;
      --datadir=*)  datadir=`echo "$arg" | sed -e 's/^[^=]*=//'`
            datadir_set=1
    ;;
      --pid-file=*) mysqld_pid_file_path=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
      --service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
    esac
  done
}

wait_for_pid () {
  verb="$1"           # created | removed
  pid="$2"            # process ID of the program operating on the pid-file
  pid_file_path="$3" # path to the PID file.

  i=0
  avoid_race_condition="by checking again"

  while test $i -ne $service_startup_timeout ; do

    case "$verb" in
      'created')
        # wait for a PID-file to pop into existence.
        test -s "$pid_file_path" && i='' && break
        ;;
      'removed')
        # wait for this PID-file to disappear
        test ! -s "$pid_file_path" && i='' && break
        ;;
      *)
        echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path"
        exit 1
        ;;
    esac

    # if server isn't running, then pid-file will never be updated
    if test -n "$pid"; then
      if kill -0 "$pid" 2>/dev/null; then
        :  # the server still runs
      else
        # The server may have exited between the last pid-file check and now.  
        if test -n "$avoid_race_condition"; then
          avoid_race_condition=""
          continue  # Check again.
        fi

        # there's nothing that will affect the file.
        log_failure_msg "The server quit without updating PID file ($pid_file_path)."
        return 1  # not waiting any more.
      fi
    fi

    echo $echo_n ".$echo_c"
    i=`expr $i + 1`
    sleep 1

  done

  if test -z "$i" ; then
    log_success_msg
    return 0
  else
    log_failure_msg
    return 1
  fi
}

# Get arguments from the my.cnf file,
# the only group, which is read from now on is [mysqld]
if test -x ./bin/my_print_defaults
then
  print_defaults="./bin/my_print_defaults"
elif test -x $bindir/my_print_defaults
then
  print_defaults="$bindir/my_print_defaults"
elif test -x $bindir/mysql_print_defaults
then
  print_defaults="$bindir/mysql_print_defaults"
else
  # Try to find basedir in /etc/my.cnf
  conf=/etc/my.cnf
  print_defaults=
  if test -r $conf
  then
    subpat='^[^=]*basedir[^=]*=\(.*\)$'
    dirs=`sed -e "/$subpat/!d" -e 's//\1/' $conf`
    for d in $dirs
    do
      d=`echo $d | sed -e 's/[     ]//g'`
      if test -x "$d/bin/my_print_defaults"
      then
        print_defaults="$d/bin/my_print_defaults"
        break
      fi
      if test -x "$d/bin/mysql_print_defaults"
      then
        print_defaults="$d/bin/mysql_print_defaults"
        break
      fi
    done
  fi

  # Hope it's in the PATH ... but I doubt it
  test -z "$print_defaults" && print_defaults="my_print_defaults"
fi

#
# Read defaults file from 'basedir'.   If there is no defaults file there
# check if it's in the old (depricated) place (datadir) and read it from there
#

extra_args=""
if test -r "$basedir/my.cnf"
then
  extra_args="-e $basedir/my.cnf"
else
  if test -r "$datadir/my.cnf"
  then
    extra_args="-e $datadir/my.cnf"
  fi
fi

parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server`

#
# Set pid file if not given
#
if test -z "$mysqld_pid_file_path"
then
  mysqld_pid_file_path=$datadir/`hostname`.pid
else
  case "$mysqld_pid_file_path" in
    /* ) ;;
    * )  mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;;
  esac
fi

case "$mode" in
  'start')
    # Start daemon

    # Safeguard (relative paths, core dumps..)
    cd $basedir

    echo $echo_n "Starting MySQL"
    if test -x $bindir/mysqld_safe
    then
      # Give extra arguments to mysqld with the my.cnf file. This script
      # may be overwritten at next upgrade.
      $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
      wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?

      # Make lock for RedHat / SuSE
      if test -w "$lockdir"
      then
        touch "$lock_file_path"
      fi

      exit $return_value
    else
      log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)"
    fi
    ;;

  'stop')
    # Stop daemon. We use a signal here to avoid having to know the
    # root password.

    if test -s "$mysqld_pid_file_path"
    then
      mysqld_pid=`cat "$mysqld_pid_file_path"`

      if (kill -0 $mysqld_pid 2>/dev/null)
      then
        echo $echo_n "Shutting down MySQL"
        kill $mysqld_pid
        # mysqld should remove the pid file when it exits, so wait for it.
        wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$?
      else
        log_failure_msg "MySQL server process #$mysqld_pid is not running!"
        rm "$mysqld_pid_file_path"
      fi

      # Delete lock for RedHat / SuSE
      if test -f "$lock_file_path"
      then
        rm -f "$lock_file_path"
      fi
      exit $return_value
    else
      log_failure_msg "MySQL server PID file could not be found!"
    fi
    ;;

  'restart')
    # Stop the service and regardless of whether it was
    # running or not, start it again.
    if $0 stop  $other_args; then
      $0 start $other_args
    else
      log_failure_msg "Failed to stop running server, so refusing to try to start."
      exit 1
    fi
    ;;

  'reload'|'force-reload')
    if test -s "$mysqld_pid_file_path" ; then
      read mysqld_pid <  "$mysqld_pid_file_path"
      kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL"
      touch "$mysqld_pid_file_path"
    else
      log_failure_msg "MySQL PID file could not be found!"
      exit 1
    fi
    ;;
  'status')
    # First, check to see if pid file exists
    if test -s "$mysqld_pid_file_path" ; then 
      read mysqld_pid < "$mysqld_pid_file_path"
      if kill -0 $mysqld_pid 2>/dev/null ; then 
        log_success_msg "MySQL running ($mysqld_pid)"
        exit 0
      else
        log_failure_msg "MySQL is not running, but PID file exists"
        exit 1
      fi
    else
      # Try to find appropriate mysqld process
      mysqld_pid=`pidof $libexecdir/mysqld`

      # test if multiple pids exist
      pid_count=`echo $mysqld_pid | wc -w`
      if test $pid_count -gt 1 ; then
        log_failure_msg "Multiple MySQL running but PID file could not be found ($mysqld_pid)"
        exit 5
      elif test -z $mysqld_pid ; then 
        if test -f "$lock_file_path" ; then 
          log_failure_msg "MySQL is not running, but lock file ($lock_file_path) exists"
          exit 2
        fi 
        log_failure_msg "MySQL is not running"
        exit 3
      else
        log_failure_msg "MySQL is running but PID file could not be found"
        exit 4
      fi
    fi
    ;;
    *)
      # usage
      basename=`basename "$0"`
      echo "Usage: $basename  {start|stop|restart|reload|force-reload|status}  [ MySQL server options ]"
      exit 1
    ;;
esac

exit 0

 

First, define the relevant parameters

basedir=
datadir=

# Default value, in seconds, afterwhich the script should timeout waiting
# for server start. 
# Value here is overriden by value in my.cnf. 
# 0 means don't wait at all
# Negative numbers mean to wait indefinitely
service_startup_timeout=900

# Lock directory for RedHat / SuSE.
lockdir='/var/lock/subsys'
lock_file_path="$lockdir/mysql"

Among them,

basedir refers to the directory where the binary compression package is decompressed, such as / usr/local/mysql.

datadir refers to a data directory

service_startup_timeout=900 defines the time limit for mysql service startup, and if the startup fails in 900s, the script exits.

lockdir='/var/lock/subsys'

For / var/lock/subsys, the online explanation is as follows, which will be used later.

Generally speaking, files under / var/lock/subsys are checked in the process of system shutdown (sending a shutdown signal, calling the process of the service itself), and each service is shut down one by one if there is no corresponding option for a running service under / var/lock/subsys. When the system shuts down, it kills the service like a normal process.

By looking at the scripts under / etc/rc.d/init.d, you can see that each service will look at the corresponding services under / var/lock/subsys when it is manipulated by itself.

Many programs need to determine whether there is an instance running at present. This directory is to let the program judge whether there is an instance running, such as xinetd. If there is this file, it means that xinetd is running, otherwise it is not. Of course, there are corresponding judgment measures in the program to really determine whether there are instances running. Usually the directory is accompanied by / var/run directory, which is used to store the ID of the corresponding instance. If you write a script, you will find that the combination of these two directories can easily determine whether many services are running, the relevant information of running, and so on.

 

Judging basedir and datadir

# Set some defaults
mysqld_pid_file_path=
if test -z "$basedir"
then
  basedir=/usr/local/mysql
  bindir=/usr/local/mysql/bin
  if test -z "$datadir"
  then
    datadir=/usr/local/mysql/data
  fi
  sbindir=/usr/local/mysql/bin
  libexecdir=/usr/local/mysql/bin
else
  bindir="$basedir/bin"
  if test -z "$datadir"
  then
    datadir="$basedir/data"
  fi
  sbindir="$basedir/sbin"
  libexecdir="$basedir/libexec"
fi

Among them,

mysqld_pid_file_path specifies the path of the PID file

- z string to determine whether a string is empty

If basedir does not display settings, the default is / usr/local/mysql, which is why many MySQL installation tutorials recommend placing mysql-related files under / usr/local/mysql.

If datadir does not display settings, the default is $basedir/data.

 

Define log_success_msg() and log_failure_msg() functions

First, determine whether the / lib/lsb/init-functions file exists, and if so, make all shell functions defined in the init-functions file valid in the current script.

If not, two functions are defined, one for printing success logs and the other for printing error logs.

In RHCS 6.7, this file does not exist and has been replaced by / etc/init.d/functions.

#
# Use LSB init script functions for printing messages, if possible
#
lsb_functions="/lib/lsb/init-functions"
if test -f $lsb_functions ; then
  . $lsb_functions
else
  log_success_msg()
  {
    echo " SUCCESS! $@"
  }
  log_failure_msg()
  {
    echo " ERROR! $@"
  }
fi

 

Transfer parameters

Pass the first parameter to mode, and the remaining parameters to other_args

PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin"
export PATH

mode=$1    # start or stop

[ $# -ge 1 ] && shift


other_args="$*"   # uncommon, but needed when called from an RPM upgrade action
           # Expected: "--skip-networking --skip-grant-tables"
           # They are not checked here, intentionally, as it is the resposibility
           # of the "spec" file author to give correct arguments only.

case `echo "testing\c"`,`echo -n testing` in
    *c*,-n*) echo_n=   echo_c=     ;;
    *c*,*)   echo_n=-n echo_c=     ;;
    *)       echo_n=   echo_c='\c' ;;
esac

 

 

Parsing parameters in configuration files

This function is covered later in the script.

It mainly involves the following parameters: basedir, - - datadir, - - pid-file, - - service-startup-timeout.

parse_server_arguments() {
  for arg do
    case "$arg" in
      --basedir=*)  basedir=`echo "$arg" | sed -e 's/^[^=]*=//'`
                    bindir="$basedir/bin"
            if test -z "$datadir_set"; then
              datadir="$basedir/data"
            fi
            sbindir="$basedir/sbin"
            libexecdir="$basedir/libexec"
        ;;
      --datadir=*)  datadir=`echo "$arg" | sed -e 's/^[^=]*=//'`
            datadir_set=1
    ;;
      --pid-file=*) mysqld_pid_file_path=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
      --service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
    esac
  done
}

 

Judging the location of my_print_defaults

First, it determines whether the executable exists in the bin directory under the current path, and if it does not, then it determines whether the $bindir (usually referred to as $basedir/bin) directory exists.

If not, it determines whether / etc/my.cnf exists and is readable, and if so, whether the basedir parameter is specified in the configuration file.

If specified, the value of the parameter is taken out and the existence of bin/my_print_defaults executable file in the directory corresponding to the value is determined.

Finally, if the my_print_defaults file is not found in the above directory,

So we set print_defaults to "my_print_defaults" in the hope that the command will be in the current PATH environment.

# Get arguments from the my.cnf file,
# the only group, which is read from now on is [mysqld]
if test -x ./bin/my_print_defaults
then
  print_defaults="./bin/my_print_defaults"
elif test -x $bindir/my_print_defaults
then
  print_defaults="$bindir/my_print_defaults"
elif test -x $bindir/mysql_print_defaults
then
  print_defaults="$bindir/mysql_print_defaults"
else
  # Try to find basedir in /etc/my.cnf
  conf=/etc/my.cnf
  print_defaults=
  if test -r $conf
  then
    subpat='^[^=]*basedir[^=]*=\(.*\)$'
    dirs=`sed -e "/$subpat/!d" -e 's//\1/' $conf`
    for d in $dirs
    do
      d=`echo $d | sed -e 's/[     ]//g'`
      if test -x "$d/bin/my_print_defaults"
      then
        print_defaults="$d/bin/my_print_defaults"
        break
      fi
      if test -x "$d/bin/mysql_print_defaults"
      then
        print_defaults="$d/bin/mysql_print_defaults"
        break
      fi
    done
  fi

  # Hope it's in the PATH ... but I doubt it
  test -z "$print_defaults" && print_defaults="my_print_defaults"
fi

 

Find the default configuration file

- r file is true if the file is readable

#
# Read defaults file from 'basedir'.   If there is no defaults file there
# check if it's in the old (depricated) place (datadir) and read it from there
#

extra_args=""
if test -r "$basedir/my.cnf"
then
  extra_args="-e $basedir/my.cnf"
else
  if test -r "$datadir/my.cnf"
  then
    extra_args="-e $datadir/my.cnf"
  fi
fi

 

Parsing parameters in configuration files

my_print_defaults is used as follows:

my_print_defaults --defaults-file=example.cnf client mysql

Read the parameter configuration of client and mysql in the configuration file.

Specifically in this script, read the configuration parameters of mysqld, server, mysql_server and mysql.server.

parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server`

 

Setting the path of pid file

- z string to determine whether a string is empty

If -- pid-file is not set in the read configuration file or the mysqld_pid_file_path parameter at the beginning of the script is not set,

The pid file is set by default under datadir, named after the host name. pid.

If this parameter is set, further judgment is needed.

If the parameter has a slash, it represents a given value with a path and can be used directly.

If there is no path in this parameter, it means that the given value is only the file name of pid, which can be set under datadir.

#
# Set pid file if not given
#
if test -z "$mysqld_pid_file_path"
then
  mysqld_pid_file_path=$datadir/`hostname`.pid
else
  case "$mysqld_pid_file_path" in
    /* ) ;;
    * )  mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;;
  esac
fi

 

Service script start option

First, switch to $basedir

Secondly, determine whether mysqld_safe in $basedir/bin is an executable file, if so, start the mysqld instance, and if not, report an error to exit. uuuuuuuuuuu

So how does the startup process work?

First, start the mysqld instance by executing the command $bindir/mysqld_safe -- datadir="$datadir" -- pid-file="$mysqld_pid_file_path"$other_args >/dev/null 2>&1 &.

Notice that mysqld_safe is actually executed in basedir, including mysql_install_db, the MySQL initialization script. It is also recommended that it be executed in basedir.

Analysis of MariaDB initialization script mysql_install_db

Then judge by wait_for_pid function, we can see the analysis of wait_for_pid function below.

After judging,

To see if the $lockdir directory is writable, create a file on it.

case "$mode" in
  'start')
    # Start daemon

    # Safeguard (relative paths, core dumps..)
    cd $basedir

    echo $echo_n "Starting MySQL"
    if test -x $bindir/mysqld_safe
    then
      # Give extra arguments to mysqld with the my.cnf file. This script
      # may be overwritten at next upgrade.
      $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
      wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?

      # Make lock for RedHat / SuSE
      if test -w "$lockdir"
      then
        touch "$lock_file_path"
      fi

      exit $return_value
    else
      log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)"
    fi
    ;;

 

wait_for_pid function

After starting the mysql instance with mysqld_safe, this parameter is called

wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?

Where $! Is used in the shell to get the PID of the last running background Process, specifically in this case, the PID of the mysqld_safe Process.

Because the first parameter i s created, the test-s "$pid_file_path" & i='& break command I s executed.

-s file is true if the length of the file is not zero

This command means that if the pid file exists, the variable i is set to null and the while loop is exited.

Then perform the following judgment.

  if test -z "$i" ; then
    log_success_msg
    return 0
  else
    log_failure_msg
    return 1
  fi

If $i is empty, the success log is printed and the script exits. Obviously, in the case of a pid file, the variable I is set to empty.

Let's look at the situation where pid files don't exist.

First, it determines whether $pid is not empty (i.e., if test-n "$pid")

If it is not empty, the pid of the process has been captured after executing mysqld_safe.

In this case, kill-0 "$pid" is used to confirm the existence of the process.

Kill-0 does not send any signals, but the system will check for errors, so it is often used to check whether a process exists. When a process does not exist, kill-0 PID will return errors.

If the process exists, no action is performed and the following action is skipped directly

echo $echo_n ".$echo_c"
i=`expr $i + 1`
sleep 1

Add variable i to 1 and sleep 1 s.

Then, continue the while loop, taking into account that mysqld_safe has been executed, but the mysqld instance is still in the process of startup and the pid file has not been created yet.

The time until $1 reaches the definition of $service_startup_timeout.

 

If during the while loop, kill-0 "$pid" is used to determine that the process no longer exists,

If the result of this judgment is still that the pid file does not exist and the process does not exist, it will be executed.

log_failure_msg "The server quit without updating PID file ($pid_file_path)."

This is the origin of the famous "The server quit without updating PID file".

wait_for_pid () {
  verb="$1"           # created | removed
  pid="$2"            # process ID of the program operating on the pid-file
  pid_file_path="$3" # path to the PID file.

  i=0
  avoid_race_condition="by checking again"

  while test $i -ne $service_startup_timeout ; do

    case "$verb" in
      'created')
        # wait for a PID-file to pop into existence.
        test -s "$pid_file_path" && i='' && break
        ;;
      'removed')
        # wait for this PID-file to disappear
        test ! -s "$pid_file_path" && i='' && break
        ;;
      *)
        echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path"
        exit 1
        ;;
    esac

    # if server isn't running, then pid-file will never be updated
    if test -n "$pid"; then
      if kill -0 "$pid" 2>/dev/null; then
        :  # the server still runs
      else
        # The server may have exited between the last pid-file check and now.  
        if test -n "$avoid_race_condition"; then
          avoid_race_condition=""
          continue  # Check again.
        fi

        # there's nothing that will affect the file.
        log_failure_msg "The server quit without updating PID file ($pid_file_path)."
        return 1  # not waiting any more.
      fi
    fi

    echo $echo_n ".$echo_c"
    i=`expr $i + 1`
    sleep 1

  done

  if test -z "$i" ; then
    log_success_msg
    return 0
  else
    log_failure_msg
    return 1
  fi
}

 

Service script stop option

First, determine whether the length of the pid file is not zero.

-s file is true if the length of the file is not zero

At this point, the pid of the mysqld process is retrieved from the pid file. Note that it is not the pid of the mysqld_safe process.

Then, determine whether the mysqld process is running properly.

If so, close the mysqld process by killing $mysqld_pid

The safest way to kill a process is to simply use the kill command, without modifiers or flags.
 
Standard kill commands usually terminate problematic processes and release process resources to the system. However, if a process starts a child process and kills only the parent process, the child process is still running, thus consuming resources. To prevent these so-called "zombie processes," it is important to ensure that all of their children are killed before the father process is killed.

Then, call wait_for_pid function to judge. In fact, the purpose of setting avoid_race_condition variable in wait_for_pid function is to stop option. It is possible that mysqld exits after checking the pid file and before checking whether the process survives.

If the mysqld process does not work properly, the "MySQL server process #$mysqld_pid is not running!" message is printed and the PID file is deleted.

If the length of the pid file is 0 when stop is executed, the "MySQL server pid file can not be found!" message will be printed.

So, in the absence of a PID file, executing the stop option through a service script does not shut down the mysqld process, at which point the mysqld process can be closed by killing $mysqld_pid.

 'stop')
    # Stop daemon. We use a signal here to avoid having to know the
    # root password.

    if test -s "$mysqld_pid_file_path"
    then
      mysqld_pid=`cat "$mysqld_pid_file_path"`

      if (kill -0 $mysqld_pid 2>/dev/null)
      then
        echo $echo_n "Shutting down MySQL"
        kill $mysqld_pid
        # mysqld should remove the pid file when it exits, so wait for it.
        wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$?
      else
        log_failure_msg "MySQL server process #$mysqld_pid is not running!"
        rm "$mysqld_pid_file_path"
      fi

      # Delete lock for RedHat / SuSE
      if test -f "$lock_file_path"
      then
        rm -f "$lock_file_path"
      fi
      exit $return_value
    else
      log_failure_msg "MySQL server PID file could not be found!"
    fi
    ;;

 

restart options for service scripts

First, the stop operation is executed, and if the stop operation is successful, the start operation is continued.

If the stop operation fails, the message "Failed to stop running server, so refusing to try to start." is output and the script exits.

  'restart')
    # Stop the service and regardless of whether it was
    # running or not, start it again.
    if $0 stop  $other_args; then
      $0 start $other_args
    else
      log_failure_msg "Failed to stop running server, so refusing to try to start."
      exit 1
    fi
    ;;

 

Service script reload option

First, determine whether the length of the PID file is 0 or not, and if not, set the value of the file to the value of the mysqld_pid variable.

The kill-HUP operation is then performed on the process.

kill -HUP pid 

pid is the process identifier. Use this command if you want to change the configuration without stopping and restarting the service. After making the necessary changes to the configuration file, issue this command to dynamically update the service configuration.

By convention, when you send a hang signal (signal 1 or HUP), most server processes (all commonly used processes) reset and reload their configuration files.

If the length of the pid file is 0, the output "MySQL pid file can not be found!"

  'reload'|'force-reload')
    if test -s "$mysqld_pid_file_path" ; then
      read mysqld_pid <  "$mysqld_pid_file_path"
      kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL"
      touch "$mysqld_pid_file_path"
    else
      log_failure_msg "MySQL PID file could not be found!"
      exit 1
    fi
    ;;

 

Service script status option

First, determine whether the length of the pid file is 0, if not, read the value in the file, and determine whether the process corresponding to the pid is running properly.

If it works properly, output "MySQL running"

If not, output "MySQL is not running, but PID file exists"

 

If the length of the pid file is 0, try to get its pid through mysqld's startup command.

At this point, there may be a mysqld program that starts multiple instances, which results in pid_count= `echo $mysqld_pid | wc-w` greater than 1.

At this point, the "Multiple MySQL running but PID file can not be found" message is output and the script exits.

If mysqld_pid is empty, it will continue to determine whether "$lock_file_path" exists, if it exists.

The message "MySQL is not running, but lock file ($lock_file_path) exists" is output.

If "$lock_file_path" does not exist, the "MySQL is not running" information is output.

If mysqld_pid equals 1, the "MySQL is running but PID file can not be found" message is output.

  'status')
    # First, check to see if pid file exists
    if test -s "$mysqld_pid_file_path" ; then 
      read mysqld_pid < "$mysqld_pid_file_path"
      if kill -0 $mysqld_pid 2>/dev/null ; then 
        log_success_msg "MySQL running ($mysqld_pid)"
        exit 0
      else
        log_failure_msg "MySQL is not running, but PID file exists"
        exit 1
      fi
    else
      # Try to find appropriate mysqld process
      mysqld_pid=`pidof $libexecdir/mysqld`

      # test if multiple pids exist
      pid_count=`echo $mysqld_pid | wc -w`
      if test $pid_count -gt 1 ; then
        log_failure_msg "Multiple MySQL running but PID file could not be found ($mysqld_pid)"
        exit 5
      elif test -z $mysqld_pid ; then 
        if test -f "$lock_file_path" ; then 
          log_failure_msg "MySQL is not running, but lock file ($lock_file_path) exists"
          exit 2
        fi 
        log_failure_msg "MySQL is not running"
        exit 3
      else
        log_failure_msg "MySQL is running but PID file could not be found"
        exit 4
      fi
    fi
    ;;

 

Other options for service scripts

If the first parameter of the script is not the above options, Usage information is output.

    *)
      # usage
      basename=`basename "$0"`
      echo "Usage: $basename  {start|stop|restart|reload|force-reload|status}  [ MySQL server options ]"
      exit 1
    ;;

 

So far, mysql's service script has been analyzed.

 

summary

In the process of starting mysql through service script, there are two conditions for reporting "The server quit without updating PID file" error

First, the pid file does not exist

Secondly, kill-0 $pid checks that the process does not exist

At this point, only the error log of mysql database can be used to locate.

 

If the service script is not adjusted, the default basedir is / usr/local/mysql and the datadir is / usr/local/mysql/data

If your mysql service is not the default path,

Explicit settings are required in this script

After testing, the following points need to be set up:

1. Setting basedir and adding conf variables

conf refers to the configuration file of mysqld. It is recommended that the values of basedir and datadir be explicitly specified in the configuration file.

Here, datadir is not set, because datadir can be retrieved through a configuration file.

But the basedir must be specified, because the my_print_deefauts command is first judged based on the basedir

basedir=/usr/local/mysql-advanced-5.6.23-linux-glibc2.5-x86_64
datadir=
conf=/usr/local/mysql-advanced-5.6.23-linux-glibc2.5-x86_64/my_3308.cnf

 

2. In line 256, add extra_args="-c $conf"

extra_args=" -e $basedir/my.cnf.bak"
if test -r "$basedir/my.cnf"
then
  extra_args="-e $basedir/my.cnf"
else
  if test -r "$datadir/my.cnf"
  then
    extra_args="-e $datadir/my.cnf"
  fi
fi
extra_args=" -c $conf"

 

3. Modify the startup parameters of 285 lines mysqld_safe

take

      $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &

Modify to

      $bindir/mysqld_safe --defaults-file="$conf" --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &

Mainly added -- defaults-file option

 

Reference resources

1. Detailed usage of test command in shell

2. Special usage of $0, $?, $! Etc. in shell

Tags: MySQL shell Linux Database

Posted on Wed, 02 Jan 2019 18:39:08 -0800 by datamodeler