国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > MySQL > 正文

mysqld_safe啟動腳本源碼閱讀、分析

2024-07-24 12:47:01
字體:
來源:轉載
供稿:網友

前幾天讀了下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表示需要killMYSQLD= # mysqld二進制可執行文件的名稱niceness=0 # 進程的調度優先級標識# 下面的變量主要用于標識不使用錯誤日志和sysloglogging=init # 日志記錄狀態,init代表初始化want_syslog=0 # 標識是否要使用syslogsyslog_tag=user='mysql' # --user選項值pid_file= # pid文件的路徑err_log= # 錯誤日志的路徑# 這兩個都是定義的syslog中標志位,在后面需要寫入日志到syslog中時使用syslog_tag_mysqld=mysqldsyslog_tag_mysqld_safe=mysqld_safetrap '' 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 <<EOFUsage: $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/loggermy_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/nullthen # 一口氣用了三個替換,分別為: # 第一步:將/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/mysqlthen 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/mysqlthen DATADIR=$MY_BASEDIR_VERSION/var# 找不到就用編譯時指定的默認值else DATADIR=/usr/local/mysql3306/varfi# 對存在兩個配置文件情況進行沖突處理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.cnfIGNORING $DATADIR/my.cnf" MYSQL_HOME=$MY_BASEDIR_VERSION elif test -r "$DATADIR/my.cnf" then log_error "WARNING: Found $DATADIR/my.cnfThe 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 fifiexport 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_defaultsthen print_defaults="./bin/my_print_defaults"elif test -x /usr/local/mysql3306/bin/my_print_defaultsthen print_defaults="/usr/local/mysql3306/bin/my_print_defaults"elif test -x /usr/local/mysql3306/bin/mysql_print_defaultsthen 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 2then SET_USER=0fi# 又對[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 fifi# 給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=syslogfi# 設置--user選項 USER_OPTION=""if test -w / -o "$USER" = "root" # 根目錄是否可寫,或者當前用戶為rootthen 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" fifisafe_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_dirfi# 如果用戶沒有制定mysqld程序的名稱,這里就默認賦值為mysqldif test -z "$MYSQLD"then MYSQLD=mysqldfi# 下面幾段分別是對 mysqld , pid , port文件選項的檢查和設置,省略100個字if test ! -x "$ledir/$MYSQLD"then log_error "The file $ledir/$MYSQLDdoes not exist or is not executable. Please cd to the mysql installationdirectory 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 1fiif test -z "$pid_file"then pid_file="$DATADIR/`/bin/hostname`.pid"else case "$pid_file" in /* ) ;; * ) pid_file="$DATADIR/$pid_file" ;; esacfiappend_arg_to_args "--pid-file=$pid_file"if test -n "$mysql_unix_port"then append_arg_to_args "--socket=$mysql_unix_port"fiif test -n "$mysql_tcp_port"then append_arg_to_args "--port=$mysql_tcp_port"fi## 接下來是關于優先級的設置#if test $niceness -eq 0then NOHUP_NICENESS="nohup"else NOHUP_NICENESS="nohup nice -$niceness"fi# 將當前的默認優先級設置為0if nohup nice > /dev/null 2>&1then # 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 fielse # 下面是測試nohup在當前系統中是否可用,不可用的話就置空NOHUP_NICENESS if nohup echo testing > /dev/null 2>&1 then : else NOHUP_NICENESS="" fifi# 指定內核文件大小if test -n "$core_file_size"then ulimit -c $core_file_sizefi## 如果已經存在一個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_filePlease remove it manually and start $0 again;mysqld daemon not started" exit 1 fifi## 下面便是拼接執行語句運行了。#cmd="$NOHUP_NICENESS"# 檢查一下命令 并進行轉義操作for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" / "--datadir=$DATADIR" "$USER_OPTION"do cmd="$cmd "`shell_quote_string "$i"`donecmd="$cmd $args"# Avoid 'nohup: ignoring input' warningtest -n "$NOHUP_NICENESS" && cmd="$cmd < /dev/null"log_notice "Starting $MYSQLD daemon with databases from $DATADIR"# 后臺循環 執行mysqldwhile truedo 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"
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 瓦房店市| 南皮县| 蒲江县| 昔阳县| 扎囊县| 定安县| 兴仁县| 太原市| 景宁| 乌兰县| 太保市| 喀喇沁旗| 城步| 巴东县| 南昌县| 石柱| 嘉祥县| 满城县| 潮安县| 滦南县| 周口市| 河北省| 泾川县| 福州市| 沙田区| 泰安市| 日照市| 海宁市| 东方市| 望城县| 宁海县| 蓬莱市| 杭州市| 武邑县| 六枝特区| 波密县| 上饶县| 尼勒克县| 且末县| 鹤山市| 泽普县|