introduction
In the previous section, we manually compiled mysql5.7.28. This section starts with the startup script mysqld_safe and continues to introduce MySQL. Compared with running mysqld directly with mysqld_safe, it has the following advantages:
- Commands are simpler. We can run mysqld_safe directly, and it will help us splicing parameters such as basedir/datadir/plugin-dir/user/log-error/pid-file to start mysqld.
- Run more reliably. It monitors mysqld, has a restart mechanism, and cleans up the pid/socket/shutdown files when the mysqld process exits abnormally.
Reminder: There are many codes ahead, if you are not patient enough, you can directly skip to the final summary and keywords!
analyze
ignore signal
trap '' 1 2 3 15
trap '' 13
Signals SIGHUP/SIGINT/SIGQUIT/SIGTERM/SIGPIPE are ignored. At this time, press Ctrl+C or kill $PID, it will not be exited. But you can kill the process with kill -9 $PID.
Setting parameters
defaults=
case "$1" in
--no-defaults|--defaults-file=*|--defaults-extra-file=*)
defaults="$1"; shift
;;
esac
If the first argument is --no-defaults or --defaults-file or --defaults-extra-file, assign to defaults and shift the argument left.
find_basedir_from_cmdline "$@"
# --basedir is already overridden on command line
if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" ; then
# Search for mysqld and set ledir
for dir in bin libexec sbin bin ; do
if test -x "$MY_BASEDIR_VERSION/$dir/mysqld" ; then
ledir="$MY_BASEDIR_VERSION/$dir"
break
fi
done
else
# Basedir should be parent dir of bindir, unless some non-standard
# layout is used
cd "`dirname $0`"
if [ -h "$0" ] ; then
realpath="`ls -l "$0" | awk '{print $NF}'`"
cd "`dirname "$realpath"`"
fi
cd ..
MY_PWD="`pwd`"
# Search for mysqld and set ledir and BASEDIR
for dir in bin libexec sbin bin ; do
if test -x "$MY_PWD/$dir/mysqld" ; then
MY_BASEDIR_VERSION="$MY_PWD"
ledir="$MY_BASEDIR_VERSION/$dir"
break
fi
done
# If we still didn't find anything, use the compiled-in defaults
if test -z "$MY_BASEDIR_VERSION" ; then
MY_BASEDIR_VERSION='/usr/local/mysql'
ledir='/usr/local/mysql/bin'
fi
fi
find_basedir_from_cmdline () {
for arg in "$@"; do
case $arg in
--basedir=*)
MY_BASEDIR_VERSION="`echo "$arg" | sed -e 's;^--[^=]*=;;'`"
# Convert to full path
cd "$MY_BASEDIR_VERSION"
if [ $? -ne 0 ] ; then
log_error "--basedir set to '$MY_BASEDIR_VERSION', however could not access directory"
exit 1
fi
MY_BASEDIR_VERSION="`pwd`"
;;
esac
done
}
If --basedir is set, assign the corresponding full path to MY_BASEDIR_VERSION. sed -e 's; –[ =]*=;;' indicates that the content between – and = is removed, that is, only the value behind –basedir= is left. ledir is set to the directory where mysqld is located. If --basedir is not set, assign the parent of the directory where the current script is located to MY_BASEDIR_VERSION. If there is no mysqld, the default value /usr/local/mysql is used, and the default value of ledir is /usr/local/mysql/bin.
if test -d $MY_BASEDIR_VERSION/data/mysql
then
DATADIR=$MY_BASEDIR_VERSION/data
# Or just give up and use our compiled-in default
else
DATADIR=/usr/local/mysql/data
fi
if test -z "$MYSQL_HOME"
then
MYSQL_HOME=$MY_BASEDIR_VERSION
fi
export MYSQL_HOME
Set DATADIR, the default is /usr/local/mysql/data. And output MYSQL_HOME as an environment variable.
if [ -n "${PLUGIN_DIR}" ]; then
plugin_dir="${PLUGIN_DIR}"
else
# Try to find plugin dir relative to basedir
for dir in lib64/mysql/plugin lib64/plugin lib/mysql/plugin lib/plugin
do
if [ -d "${MY_BASEDIR_VERSION}/${dir}" ]; then
plugin_dir="${MY_BASEDIR_VERSION}/${dir}"
break
fi
done
# Give up and use compiled-in default
if [ -z "${plugin_dir}" ]; then
plugin_dir='/usr/local/mysql/lib/plugin'
fi
fi
plugin_dir="${plugin_dir}${PLUGIN_VARIANT}"
Look for the plugin directory, the default is /usr/local/mysql/lib/plugin.
USER_OPTION=""
if test -w / -o "$USER" = "root"
then
if test "$user" != "root" -o $SET_USER = 1
then
USER_OPTION="--user=$user"
fi
if test -n "$open_files"
then
ulimit -n $open_files
fi
fi
if test -n "$open_files"
then
append_arg_to_args "--open-files-limit=$open_files"
fi
If you execute mysqld_safe without setting parameters, the default user is mysql, so USER_OPTION="–user=mysql" here. open_files need to set --open-files-limit, the default is empty.
if test -z "$pid_file"
then
pid_file="$DATADIR/`hostname`.pid"
pid_file_append="`hostname`.pid"
else
pid_file_append="$pid_file"
case "$pid_file" in
/* ) ;;
* ) pid_file="$DATADIR/$pid_file" ;;
esac
fi
append_arg_to_args "--pid-file=$pid_file_append"
if test -n "$mysql_unix_port"
then
append_arg_to_args "--socket=$mysql_unix_port"
fi
if test -n "$mysql_tcp_port"
then
append_arg_to_args "--port=$mysql_tcp_port"
fi
Append parameters --pid-file/--socket/--port. If mysql_unix_port and mysql_tcp_port are not specified at startup, --socket and --port are not appended.
build command
cmd="`mysqld_ld_preload_text`$NOHUP_NICENESS"
for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \
"--datadir=$DATADIR" "--plugin-dir=$plugin_dir" "$USER_OPTION"
do
cmd="$cmd "`shell_quote_string "$i"`
done
cmd="$cmd $args"
# Avoid 'nohup: ignoring input' warning
test -n "$NOHUP_NICENESS" && cmd="$cmd < /dev/null"
Splice startup commands and parameters, and test, where –log-error and –pid-file come from $args.
nohup /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=DESKTOP-7GPR56L.err --pid-file=DESKTOP-7GPR56L.pid
Excuting an order
Next is the main loop while true
while true
do
start_time=`date +%M%S`
eval_log_error "$cmd"
...
done
eval_log_error () {
cmd="$1"
case $logging in
file)
if [ -w / -o "$USER" = "root" ]; then
cmd="$cmd > /dev/null 2>&1"
else
cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1"
fi
;;
syslog)
cmd="$cmd --log-syslog=1 --log-syslog-facility=$syslog_facility '--log-syslog-tag=$syslog_tag' > /dev/null 2>&1"
;;
both)
if [ -w / -o "$USER" = "root" ]; then
cmd="$cmd --log-syslog=1 --log-syslog-facility=$syslog_facility '--log-syslog-tag=$syslog_tag' > /dev/null 2>&1"
else
cmd="$cmd --log-syslog=1 --log-syslog-facility=$syslog_facility '--log-syslog-tag=$syslog_tag' >> "`shell_quote_string "$err_log"`" 2>&1"
fi
;;
*)
echo "Internal program error (non-fatal):" \
" unknown logging method '$logging'" >&2
;;
esac
#echo "Running mysqld: [$cmd]"
eval "$cmd"
}
In the eval_log_error function, continue to splice parameters and execute them. This time, the log is redirected to cmd="$cmd > /dev/null 2>&1". The final command is as follows:
nohup /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=DESKTOP-7GPR56L.err --pid-file=DESKTOP-7GPR56L.pid < /dev/null > /dev/null 2>&1
exception handling
if test ! -f "$pid_file" # This is removed if normal shutdown
then
break
else # self's mysqld crashed or other's mysqld running
PID=`cat "$pid_file"`
if kill -0 $PID > /dev/null 2> /dev/null
then # true when above pid belongs to a running mysqld process
log_error "A mysqld process with pid=$PID is already running. Aborting!!"
exit 1
fi
fi
if test -f "$pid_file.shutdown" # created to signal that it must stop
then
log_notice "$pid_file.shutdown present. The server will not restart."
break
fi
The pid file is located in the data directory by default, and the content is the PID. The pid file is deleted on a normal shutdown. If it is determined that there is already a running mysqld process, the script exits. If there is a shutdown file, exit the loop.
if test $end_time -gt 0 -a $have_sleep -gt 0
then
# throttle down the fast restarts
if test $end_time -eq $start_time
then
fast_restart=`expr $fast_restart + 1`
if test $fast_restart -ge $max_fast_restarts
then
log_notice "The server is respawning too fast. Sleeping for 1 second."
sleep 1
sleep_state=$?
if test $sleep_state -gt 0
then
log_notice "The server is respawning too fast and no working sleep command. Turning off trottling."
have_sleep=0
fi
fast_restart=0
fi
else
fast_restart=0
fi
fi
If frequent freezes occur, try to sleep for 1 second to wait for the next cycle restart, and record the log.
if true && test $KILL_MYSQLD -eq 1
then
# Test if one process was hanging.
# This is only a fix for Linux (running as base 3 mysqld processes)
# but should work for the rest of the servers.
# The only thing is ps x => redhat 5 gives warnings when using ps -x.
# kill -9 is used or the process won't react on the kill.
numofproces=`ps xaww | grep -v "grep" | grep "$ledir/$MYSQLD\>" | grep -c "pid-file=$pid_file"`
log_notice "Number of processes running now: $numofproces"
I=1
while test "$I" -le "$numofproces"
do
PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'`
for T in $PROC
do
break
done
# echo "TEST $I - $T **"
if kill -9 $T
then
log_error "$MYSQLD process hanging, pid $T - killed"
else
break
fi
I=`expr $I + 1`
done
fi
if [ ! -h "$pid_file" ]; then
rm -f "$pid_file"
fi
if [ ! -h "$safe_mysql_unix_port" ]; then
rm -f "$safe_mysql_unix_port"
fi
if [ ! -h "$pid_file.shutdown" ]; then
rm -f "$pid_file.shutdown"
fi
If KILL_MYSQLD=1, kill all mysqld processes and clean up the three files of pid/socket/shutdown.
Summarize
Excellent projects always have something worth learning, such as learning how to write mysqld_safe, we can learn its trap, nohup and log redirection, etc., to simply implement a script for a daemon to start a background process. In addition, at this time, there is no need to worry about the end of the process caused by the exit of the user terminal. Of course, there are many ways, both service and screen can achieve this effect.
Key words
Ignore signals; set parameters; generate commands; execute commands; exception handling