mysqld_safe啟動劇本源碼浏覽、剖析。本站提示廣大學習愛好者:(mysqld_safe啟動劇本源碼浏覽、剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是mysqld_safe啟動劇本源碼浏覽、剖析正文
頭幾天讀了下mysqld_safe劇本,小我感到照樣收成蠻年夜的,個中過細的交卸了MySQL數據庫的啟動流程,包含查找MySQL相干目次,解析設置裝備擺設文件和最初若何挪用mysqld法式來啟動實例等,有著不錯的參考價值;與此同時,劇本中觸及了許多shell編程中的小技能,像變量解析,sed調換本義,過程優先級的斷定和無處不在test構造等等,看成Linux shell的進修素材照樣異常適合的,上面是我的情況:
數據庫版本: MySQL 5.1.45
操作體系版本: Red Hat Enterprise Linux AS release 4 (Nahant Update 3)
MySQL基目次: /usr/local/mysql3306
設置裝備擺設文件目次: /usr/local/mysql3306/etc
數據庫是裝置好了的,代碼以下:
#!/bin/sh
# 一些狀況變量的界說
KILL_MYSQLD=1; # 試圖kill過剩的mysqld_safe法式,1表現須要kill
MYSQLD= # mysqld二進制可履行文件的稱號
niceness=0 # 過程的調劑優先級標識
# 上面的變量重要用於標識不應用毛病日記和syslog
logging=init # 日記記載狀況,init代表初始化
want_syslog=0 # 標識能否要應用syslog
syslog_tag=
user='mysql' # --user選項值
pid_file= # pid文件的途徑
err_log= # 毛病日記的途徑
# 這兩個都是界說的syslog中標記位,在前面須要寫入日記到syslog中時應用
syslog_tag_mysqld=mysqld
syslog_tag_mysqld_safe=mysqld_safe
trap '' 1 2 3 15 # 不許可法式在終端上被人打斷(包含掛起,中止,加入,體系終止的情況)
umask 007 # 默許權限770,其他組用戶對該法式創立的文件沒有任何權限
# defaults變量記錄應用的設置裝備擺設文件的信息
defaults=
case "$1" in
--no-defaults|--defaults-file=*|--defaults-extra-file=*)
defaults="$1"; shift
;;
esac
# usage()函數:應用--help選項時輸入的應用贊助信息
usage () {
cat <<EOF
Usage: $0 [OPTIONS]
--no-defaults Don't read the system defaults file
--defaults-file=FILE Use the specified defaults file
--defaults-extra-file=FILE Also use defaults from the specified file
--ledir=DIRECTORY Look for mysqld in the specified directory
--open-files-limit=LIMIT Limit the number of open files
--core-file-size=LIMIT Limit core files to the specified size
--timezone=TZ Set the system timezone
--mysqld=FILE Use the specified file as mysqld
--mysqld-version=VERSION Use "mysqld-VERSION" as mysqld
--nice=NICE Set the scheduling priority of mysqld
--skip-kill-mysqld Don't try to kill stray mysqld processes
--syslog Log messages to syslog with 'logger'
--skip-syslog Log messages to error log (default)
--syslog-tag=TAG Pass -t "mysqld-TAG" to 'logger'
All other options are passed to the mysqld program.
EOF
exit 1
}
# my_which的感化相當於which,經由過程檢索$PATH中的途徑,打印出敕令的全途徑
# 這個函數就在前面一個處所用到了,就是my_which logger,意思同等於轉換logger為/usr/bin/logger
my_which ()
{
save_ifs="${IFS-UNSET}" # 保留以後的內建分隔符,用於前面重置IFS
IFS=: # 應用 : 來朋分PATH中的途徑
ret=0
for file # 這類寫法同等於for file in &*
do
for dir in $PATH
do
if [ -f "$dir/$file" ]
then
echo "$dir/$file"
continue 2 # continue 第 2 層, 這裡就是跳出外層輪回了
fi
done
ret=1 # signal an error
break
done
# 將設置過的IFS重置歸去
if [ "$save_ifs" = UNSET ]
then
unset IFS
else
IFS="$save_ifs"
fi
return $ret # Success
}
# 日記輸入函數,這是個原型,前面被log_error和log_notice函數援用
log_generic () {
# priority 代表日記信息的分類,從前面的兩個函數可知有:daemon.error和daemon.notice兩品種別
priority="$1"
shift
# 日記中記載的msg前綴格局: 時光 + mysqld_safe ,相似於體系日記的記載格局
msg="`date +'%y%m%d %H:%M:%S'` mysqld_safe $*"
echo "$msg"
case $logging in
init) ;; # 初始化狀況時,只在敕令行輸入msg信息,不記載日記
file) echo "$msg" >> "$err_log" ;; # 記載到err_log中
syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;; # 應用logger記載到體系日記中
*)
echo "Internal program error (non-fatal):" \
" unknown logging method '$logging'" >&2
;;
esac
}
# 上面兩個函數是對log_generic函數中分歧分類的援用
log_error () {
log_generic daemon.error "$@" >&2
}
log_notice () {
log_generic daemon.notice "$@"
}
# 前面就是用它啟動的mysqld,經由過程logging變量辨別記載日記的類型,分毛病日記和體系日記syslog兩種
# 最初的eval敕令會解析 $cmd 中的值並履行敕令
eval_log_error () {
cmd="$1"
case $logging in
file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;;
syslog)
cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error"
;;
*)
echo "Internal program error (non-fatal):" \
" unknown logging method '$logging'" >&2
;;
esac
#echo "Running mysqld: [$cmd]"
eval "$cmd"
}
# 本義函數,用於在非"a-z","A-Z","09",'/','_','.','=','-'的特別字符前加上一個"\"
# sed中的\1代表援用後面\(\)中婚配的值
shell_quote_string() {
echo "$1" | sed -e 's,\([^a-zA-Z0-9/_.=-]\),\\\1,g'
}
# 該函數用於解析設置裝備擺設文件中的選項,並賦值給響應的變量
parse_arguments() {
pick_args=
if test "$1" = PICK-ARGS-FROM-ARGV
then
pick_args=1
shift
fi
for arg do
# 掏出參數值,好比 --port=3306 成果為: val = 3306 留意這裡sed中應用;來朋分,同等於/
val=`echo "$arg" | sed -e "s;--[^=]*=;;"`
case "$arg" in
# 將參數值傳遞給對應的變量
--basedir=*) MY_BASEDIR_VERSION="$val" ;;
--datadir=*) DATADIR="$val" ;;
--pid-file=*) pid_file="$val" ;;
--user=*) user="$val"; SET_USER=1 ;;
# 有些值能夠曾經在my.cnf設置裝備擺設文件的[mysqld_safe]組下設置了
# 某些值會被敕令行上指定的選項值籠罩
--log-error=*) err_log="$val" ;;
--port=*) mysql_tcp_port="$val" ;;
--socket=*) mysql_unix_port="$val" ;;
# 接上去這幾個特別的選項在設置裝備擺設文件的[mysqld_safe]組中是必需設置的
# 我沒設置裝備擺設這個組,所以就用不到了(應用mysqld中的默許)
--core-file-size=*) core_file_size="$val" ;;
--ledir=*) ledir="$val" ;;
--mysqld=*) MYSQLD="$val" ;;
--mysqld-version=*)
if test -n "$val"
then
MYSQLD="mysqld-$val"
else
MYSQLD="mysqld"
fi
;;
--nice=*) niceness="$val" ;;
--open-files-limit=*) open_files="$val" ;;
--skip-kill-mysqld*) KILL_MYSQLD=0 ;;
--syslog) want_syslog=1 ;;
--skip-syslog) want_syslog=0 ;;
--syslog-tag=*) syslog_tag="$val" ;;
--timezone=*) TZ="$val"; export TZ; ;; # 失效了一下時區設置
--help) usage ;; # 挪用了usage函數,輸入贊助信息
*)
if test -n "$pick_args"
then
# 將其他敕令行參數值附加到$arg的前面
append_arg_to_args "$arg"
fi
;;
esac
done
}
########################################
# 正式任務開端了!!
########################################
#
# 上面兩段是在尋覓基目次和mysqld地點目次
#
# 找到/usr/local/mysql3306/share/mysql目次,應用relpkgdata來記載絕對途徑和相對途徑
# 這個grep其實應當是想斷定一下share/mysql是否是顯示的相對途徑,不曉得這麼寫的意義在哪裡。
if echo '/usr/local/mysql3306/share/mysql' | grep '^/usr/local/mysql3306' > /dev/null
then
# 一口吻用了三個調換,分離為:
# 第一步:將/usr/local/mysql3306轉換為空
# 第二步:將/share/mysql開首的/轉換為空
# 第三步:在share/mysql開首加上./,成果即:./share/mysql
relpkgdata=`echo '/usr/local/mysql3306/share/mysql' | sed -e 's,^/usr/local/mysql3306,,' -e 's,^/,,' -e 's,^,./,'`
else
relpkgdata='/usr/local/mysql3306/share/mysql'
fi
# 這一段都是在找mysqld文件,分離斷定了libexec和bin目次
# 找不到就應用編譯時的默許值
MY_PWD=`pwd`
if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION"
then
if test -x "$MY_BASEDIR_VERSION/libexec/mysqld"
then
ledir="$MY_BASEDIR_VERSION/libexec"
else
ledir="$MY_BASEDIR_VERSION/bin"
fi
# 這裡對errmsg.sys文件停止了斷定,小我以為這是為了確認以後目次為一個mysql裝置基目次
elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/bin/mysqld"
then
MY_BASEDIR_VERSION="$MY_PWD"
ledir="$MY_PWD/bin"
elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld"
then
MY_BASEDIR_VERSION="$MY_PWD"
ledir="$MY_PWD/libexec"
else
MY_BASEDIR_VERSION='/usr/local/mysql3306'
ledir='/usr/local/mysql3306/libexec'
fi
#
# 接上去是找到設置裝備擺設文件和數據文件目次
#
# 找到設置裝備擺設文件目次
# 我的是放在了etc/目次下,mysqld法式是會讀取到的
#
# 可以從my_print_defaults劇本中取得默許的讀取my.cnf次序,以下
# Default options are read from the following files in the given order:
# /etc/my.cnf /etc/mysql/my.cnf /home/mysql/mysql_master/etc/my.cnf ~/.my.cnf
# 或許可使用strace -e open libexec/mysqld 2>&1 | grep my.cnf檢查
if test -d $MY_BASEDIR_VERSION/data/mysql
then
DATADIR=$MY_BASEDIR_VERSION/data
if test -z "$defaults" -a -r "$DATADIR/my.cnf"
then
defaults="--defaults-extra-file=$DATADIR/my.cnf"
fi
# 接上去找到數據文件的目次
elif test -d $MY_BASEDIR_VERSION/var/mysql
then
DATADIR=$MY_BASEDIR_VERSION/var
# 找不到就用編譯時指定的默許值
else
DATADIR=/usr/local/mysql3306/var
fi
# 對存在兩個設置裝備擺設文件情形停止抵觸處置
if test -z "$MYSQL_HOME"
then
if test -r "$MY_BASEDIR_VERSION/my.cnf" && test -r "$DATADIR/my.cnf"
then
# 優先斟酌 $MY_BASEDIR_VERSION/my.cnf 文件
log_error "WARNING: Found two instances of my.cnf -
$MY_BASEDIR_VERSION/my.cnf and
$DATADIR/my.cnf
IGNORING $DATADIR/my.cnf"
MYSQL_HOME=$MY_BASEDIR_VERSION
elif test -r "$DATADIR/my.cnf"
then
log_error "WARNING: Found $DATADIR/my.cnf
The data directory is a deprecated location for my.cnf, please move it to
$MY_BASEDIR_VERSION/my.cnf"
MYSQL_HOME=$DATADIR
else
MYSQL_HOME=$MY_BASEDIR_VERSION
fi
fi
export MYSQL_HOME
#
# 上面是應用bin/my_print_defaults讀取my.cnf文件中的設置裝備擺設信息([mysqld] and [mysqld_safe])
# 而且和敕令行中傳入的參數停止歸並
# 先是找到my_print_defaults履行文件 又是各類途徑斷定
if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults"
then
print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults"
elif test -x ./bin/my_print_defaults
then
print_defaults="./bin/my_print_defaults"
elif test -x /usr/local/mysql3306/bin/my_print_defaults
then
print_defaults="/usr/local/mysql3306/bin/my_print_defaults"
elif test -x /usr/local/mysql3306/bin/mysql_print_defaults
then
print_defaults="/usr/local/mysql3306/bin/mysql_print_defaults"
else
print_defaults="my_print_defaults"
fi
# 這個函數可以將一個指定的參數附加到$arg中(在此同時履行了本義操作)
append_arg_to_args () {
args="$args "`shell_quote_string "$1"`
}
args=
# 這裡SET_USER=2是針對上面一條parse_arguments來講的
# 由於假如在緊接著的parse_arugments函數中設置了--user的值,那末SET_USER就會變成1,表現--user以被設置裝備擺設
# 固然假如沒有讀取到--user的值,就是說--user沒有設置裝備擺設,那末會在前面的if構造中設置SET_USER為0
# 如許在前面的斷定構造中,SET_USER的值 0代表沒有設置裝備擺設--user的值,1代表曾經設置裝備擺設
SET_USER=2
# 解析設置裝備擺設文件中的參數,應用--loose-verbose來過濾[mysqld]和[server]組中的內容
parse_arguments `$print_defaults $defaults --loose-verbose mysqld server`
if test $SET_USER -eq 2
then
SET_USER=0
fi
# 又對[safe_mysqld]和[mysqld_safe]組中的內容停止了過濾讀取
# 在我的設置裝備擺設文件中曾經沒有這兩個組了,估量是為兼容舊版本的須要
parse_arguments `$print_defaults $defaults --loose-verbose mysqld_safe safe_mysqld`
# 用敕令行輸出選項 $@ 來籠罩設置裝備擺設文件中的選項 機靈
parse_arguments PICK-ARGS-FROM-ARGV "$@"
#
# 上面是logging對象的應用
#
# 斷定logger對象能否可用
if [ $want_syslog -eq 1 ]
then
my_which logger > /dev/null 2>&1
if [ $? -ne 0 ]
then
log_error "--syslog requested, but no 'logger' program found. Please ensure that 'logger' is in your PATH, or do not specify the --syslog option to mysqld_safe."
exit 1
fi
fi
# 給err_log更名字。。。
if [ -n "$err_log" -o $want_syslog -eq 0 ]
then
if [ -n "$err_log" ]
then
# 上面是為err_log添加一個.err後綴(假如如今名字沒有後綴)
# 假如不設置這個後綴,mysqld_safe和mysqld法式會將日記寫入分歧的文件中
# 由於在 mysqld 法式中,它將辨認帶有.的文件名為毛病日記(劇本正文上說的)
# 這裡的expr是辨認文件名中“.”後面的字符總數目(包含.),假如沒有設置後綴,前往就是0了
if expr "$err_log" : '.*\.[^/]*$' > /dev/null
then
:
else
err_log="$err_log".err
fi
case "$err_log" in
/* ) ;;
* ) err_log="$DATADIR/$err_log" ;;
esac
else
err_log=$DATADIR/`/bin/hostname`.err
fi
# 追加毛病日記的地位選項
append_arg_to_args "--log-error=$err_log"
# 收回毛病提醒:不要應用syslog
if [ $want_syslog -eq 1 ]
then
log_error "Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect."
fi
# Log to err_log file
log_notice "Logging to '$err_log'."
logging=files # 正式把logging改成files 應用毛病日記來記載日記
# 這個分支就是應用syslog的辦法了
else
if [ -n "$syslog_tag" ]
then
# 設置各個syslog的應用標記位
syslog_tag=`echo "$syslog_tag" | sed -e 's/[^a-zA-Z0-9_-]/_/g'`
syslog_tag_mysqld_safe="${syslog_tag_mysqld_safe}-$syslog_tag"
syslog_tag_mysqld="${syslog_tag_mysqld}-$syslog_tag"
fi
log_notice "Logging to syslog."
logging=syslog
fi
# 設置--user選項
USER_OPTION=""
if test -w / -o "$USER" = "root" # 根目次能否可寫,或許以後用戶為root
then
if test "$user" != "root" -o $SET_USER = 1
then
USER_OPTION="--user=$user"
fi
# 創立毛病日記,並將日記受權給指定的用戶
if [ $want_syslog -eq 0 ]; then
touch "$err_log"
chown $user "$err_log"
fi
# 這裡它還對以後用戶做了ulimit設置,包含可以翻開的文件數目--open_files-limit選項
if test -n "$open_files"
then
ulimit -n $open_files
append_arg_to_args "--open-files-limit=$open_files"
fi
fi
safe_mysql_unix_port={mysql_unix_port:-${MYSQL_UNIX_PORT:-/usr/local/mysql3306/tmp/mysql.sock}}
# 確保 $safe_mysql_unix_port 目次是存在的
mysql_unix_port_dir=`dirname $safe_mysql_unix_port`
if [ ! -d $mysql_unix_port_dir ]
then
mkdir $mysql_unix_port_dir
chown $user $mysql_unix_port_dir
chmod 755 $mysql_unix_port_dir
fi
# 假如用戶沒有制訂mysqld法式的稱號,這裡就默許賦值為mysqld
if test -z "$MYSQLD"
then
MYSQLD=mysqld
fi
# 上面幾段分離是對 mysqld , pid , port文件選項的檢討和設置,省略100個字
if test ! -x "$ledir/$MYSQLD"
then
log_error "The file $ledir/$MYSQLD
does not exist or is not executable. Please cd to the mysql installation
directory and restart this script from there as follows:
./bin/mysqld_safe&
See http://dev.mysql.com/doc/mysql/en/mysqld-safe.html for more information"
exit 1
fi
if test -z "$pid_file"
then
pid_file="$DATADIR/`/bin/hostname`.pid"
else
case "$pid_file" in
/* ) ;;
* ) pid_file="$DATADIR/$pid_file" ;;
esac
fi
append_arg_to_args "--pid-file=$pid_file"
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
#
# 接上去是關於優先級的設置
#
if test $niceness -eq 0
then
NOHUP_NICENESS="nohup"
else
NOHUP_NICENESS="nohup nice -$niceness"
fi
# 將以後的默許優先級設置為0
if nohup nice > /dev/null 2>&1
then
# normal_niceness記錄默許的調劑優先級
normal_niceness=`nice`
# nohup_niceness記錄應用nohup履行方法的調劑優先級
nohup_niceness=`nohup nice 2>/dev/null`
numeric_nice_values=1
# 這個for是為了檢討$normal_niceness $nohup_niceness兩個變量值的正當性
for val in $normal_niceness $nohup_niceness
do
case "$val" in
-[0-9] | -[0-9][0-9] | -[0-9][0-9][0-9] | \
[0-9] | [0-9][0-9] | [0-9][0-9][0-9] )
;;
* )
numeric_nice_values=0 ;;
esac
done
# 這個斷定構造很主要
# 它包管了應用nohup履行的mysqld法式在調劑優先級上不會低於直接履行mysqld法式的方法
if test $numeric_nice_values -eq 1
then
nice_value_diff=`expr $nohup_niceness - $normal_niceness`
if test $? -eq 0 && test $nice_value_diff -gt 0 && \
nice --$nice_value_diff echo testing > /dev/null 2>&1
then
# 進入分支解釋$nohup_niceness的值比$normal_niceness年夜,即nohup履行方法調劑優先級比正常履行方法低
# 這是不願望看到的,所以上面就工資的晉升了nohup的優先級(下降niceness的值)
niceness=`expr $niceness - $nice_value_diff`
NOHUP_NICENESS="nice -$niceness nohup"
fi
fi
else
# 上面是測試nohup在以後體系中能否可用,弗成用的話就置空NOHUP_NICENESS
if nohup echo testing > /dev/null 2>&1
then
:
else
NOHUP_NICENESS=""
fi
fi
# 指定內核文件年夜小
if test -n "$core_file_size"
then
ulimit -c $core_file_size
fi
#
# 假如曾經存在一個pid文件,則檢討能否有曾經啟動的mysqld_safe過程
if test -f "$pid_file"
then
PID=`cat "$pid_file"`
if /bin/kill -0 $PID > /dev/null 2> /dev/null
then
if /bin/ps wwwp $PID | grep -v " grep" | grep -v mysqld_safe | grep -- "$MYSQLD" > /dev/null
then
log_error "A mysqld process already exists"
exit 1
fi
fi
# 上面是處置方法:刪除舊的pid文件並報錯
rm -f "$pid_file"
if test -f "$pid_file"
then
log_error "Fatal error: Can't remove the pid file:
$pid_file
Please remove it manually and start $0 again;
mysqld daemon not started"
exit 1
fi
fi
#
# 上面就是拼接履行語句運轉了。
#
cmd="$NOHUP_NICENESS"
# 檢討一下敕令 並停止本義操作
for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \
"--datadir=$DATADIR" "$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"
log_notice "Starting $MYSQLD daemon with databases from $DATADIR"
# 後台輪回 履行mysqld
while true
do
rm -f $safe_mysql_unix_port "$pid_file" # 保險起見,又刪除一次pid文件
# 挪用eval_log_error函數,傳入$cmd參數的值,最初應用eval敕令履行了啟動mysqld
eval_log_error "$cmd"
if test ! -f "$pid_file" # 沒有勝利創立pid文件,則加入分支
then
break
fi
# mysqld_safe曾經啟動的處置辦法,包管只要一個mysqld_safe法式啟動
if true && test $KILL_MYSQLD -eq 1
then
# 統計啟動的mysqld過程的數量
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 mysqld_safe法式的輸入 第一個數字即為過程ID
PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'`
# 應用T來獲得過程ID
for T in $PROC
do
break
done
# kill失落該個mysqld_safe法式
if kill -9 $T
then
log_error "$MYSQLD process hanging, pid $T - killed"
else
break
fi
# 每干失落一個mysqld_safe就把I加一,如許沒有過剩的mysqld_safe時便可以跳出輪回了
I=`expr $I + 1`
done
fi
log_notice "mysqld restarted"
done
# 結束撒花
log_notice "mysqld from pid file $pid_file ended"