本帖最后由 pig2 于 2014-11-21 15:08 编辑
问题导读
1、return proc.pid这段代码主要做了哪些事?
2、launch方法依次启动了哪些服务?
3、如何通过配置文件来判断服务是否已经启动?
继续看方法def start(self, **kwargs):
- def start(self, **kwargs):
- """
- 启动一个服务;
- """
- setup_env()
- status = 0
-
- for server in self.servers:
- server.launch(**kwargs)
- if not kwargs.get('daemon', True):
- for server in self.servers:
- try:
- status += server.interact(**kwargs)
- except KeyboardInterrupt:
- print _('\nuser quit')
- self.stop(**kwargs)
- break
- elif kwargs.get('wait', True):
- for server in self.servers:
- status += server.wait(**kwargs)
- return status 我们先来看代码:
- for server in self.servers:
- server.launch(**kwargs)前面分析知道,在类Manager初始化的过程中,得到:
- self.server = ['proxy-server', 'account-server', 'container-server', 'object-server']
复制代码
所以这个for循环实现了通过调用launch方法依次启动这四个服务;
注:示例:
- kwargs = {'run_dir': '/var/run/swift', 'daemon': True, 'verbose': False, 'number': 0, 'kill_wait': 15, 'graceful': False, 'once': False, 'wait': True}
复制代码
我们具体来看方法launch:
- def launch(self, **kwargs):
- """
- Collect conf files and attempt to spawn the processes for this server
- """
- conf_files = self.conf_files(**kwargs)
- if not conf_files:
- return []
- # 获取运行的pids;
- # 返回一个pids映射到pid_files的字典;
- pids = self.get_running_pids(**kwargs)
- already_started = False
- for pid, pid_file in pids.items():
-
- # 转换pid_file到相应的conf_file;
- # 返回pid_file对应的conf_file;
- conf_file = self.get_conf_file_name(pid_file)
-
- if conf_file in conf_files:
- already_started = True
- print _("%s running (%s - %s)") % (self.server, pid, conf_file)
-
- elif not kwargs.get('number', 0):
- already_started = True
- print _("%s running (%s - %s)") % (self.server, pid, pid_file)
- if already_started:
- print _("%s already started...") % self.server
- return []
-
- if self.server not in START_ONCE_SERVERS:
- kwargs['once'] = False
- pids = {}
- for conf_file in conf_files:
- if kwargs.get('once'):
- msg = _('Running %s once') % self.server
- else:
- msg = _('Starting %s') % self.server
- print '%s...(%s)' % (msg, conf_file)
-
- try:
- # conf_file:/etc/swift/proxy-server.conf
- # kwargs = {'run_dir':'/var/run/swift','damon':True,'verbose':False,'number':0,'kill_wait':15,'graceful':False,'once':False,'wait':True}
- pid = self.spawn(conf_file, **kwargs)
- except OSError, e:
- if e.errno == errno.ENOENT:
- # TODO: should I check if self.cmd exists earlier?
- print _("%s does not exist") % self.cmd
- break
- pids[pid] = conf_file
- return pids这个方法主要实现了根据服务获取具体的配置文件,并试图通过调用方法spawn来为服务的运行启动一个子进程,方法的最后返回了这个子进程的进程号;
复制代码
首先来看代码:
- conf_files = self.conf_files(**kwargs)
- if not conf_files:
- return []
复制代码
实现的是通过传入的kwargs获取服务匹配的配置文件,如果没有配置文件直接返回;
调试运行示例:
- conf_files = ['/etc/swift/proxy-server.conf']
复制代码
再来看代码:
- # 获取运行的pids;
- # 返回一个pids映射到pid_files的字典;
- pids = self.get_running_pids(**kwargs)调试运行示例:
- pids = {10955: '/var/run/swift/proxy-server.pid'}
- 可见这条语句返回的是正在运行进程的pid号,和具体匹配的pid_file的路径,为后面判断要启动的服务是否已经运行做准备;
复制代码
看下面一段代码,通过配置文件来判断服务是否已经启动:
- already_started = False
- for pid, pid_file in pids.items():
-
- # 转换pid_file到相应的conf_file;
- # 返回pid_file对应的conf_file;
- conf_file = self.get_conf_file_name(pid_file)
-
- if conf_file in conf_files:
- already_started = True
- print _("%s running (%s - %s)") % (self.server, pid, conf_file)
-
- elif not kwargs.get('number', 0):
- already_started = True
- print _("%s running (%s - %s)") % (self.server, pid, pid_file)
- if already_started:
- print _("%s already started...") % self.server
- return []前面我们通过kwargs获取了正在运行的服务的pid值和相应的pid_file,比如:
- pids = {10955: '/var/run/swift/proxy-server.pid'}
复制代码
这里遍历pids这个字典,做了以下几件事:
(1)通过pid_file获取对应服务的配置文件conf_file;
(2)判断获取的conf_file是否包含在已经获取的conf_files中,如果是的话,说明要启动的这个服务已经处于运行状态;
(3)如果(2)判断为假,继续判断传进来的参数kwargs中,是否包含'number',这一选项,它的值是否为0,如果是的话,也说明要启动的这个服务已经处于运行状态;
最后,如果already_started的值为真,说明服务已经运行了,所以打印输出提示后,直接返回;
继续来看代码,如何为服务来启动一个子进程:
- pids = {}
- for conf_file in conf_files:
- if kwargs.get('once'):
- msg = _('Running %s once') % self.server
- else:
- msg = _('Starting %s') % self.server
- print '%s...(%s)' % (msg, conf_file)
-
- try:
- # conf_file:/etc/swift/proxy-server.conf
- # kwargs = {'run_dir':'/var/run/swift','damon':True,'verbose':False,'number':0,'kill_wait':15,'graceful':False,'once':False,'wait':True}
- pid = self.spawn(conf_file, **kwargs)
- except OSError, e:
- if e.errno == errno.ENOENT:
- # TODO: should I check if self.cmd exists earlier?
- print _("%s does not exist") % self.cmd
- break
- pids[pid] = conf_file这段代码是遍历conf_files,根据每个配置文件来为每个服务启动一个子进程,并返回相应的进程号,最后把启动的子进程的进程号pid和相应服务的配置文件conf_file一一对应起来;
- 为服务启动子进程主要是通过语句:pid = self.spawn(conf_file, **kwargs)
复制代码
我们具体来看方法spawn:
- def spawn(self, conf_file, once=False, wait=True, daemon=True, **kwargs):
- """
- 为服务启动一个子进程;
- 返回衍生进程的pid;
-
- # 调用之一传进来的参数:
- # conf_file:/etc/swift/proxy-server.conf
- # kwargs = {'run_dir':'/var/run/swift','damon':True,'verbose':False,'number':0,'kill_wait':15,'graceful':False,'once':False,'wait':True}
- """
- args = [self.cmd, conf_file]
-
- if once:
- args.append('once')
-
- if not daemon:
- # ask the server to log to console
- args.append('verbose')
- # figure out what we're going to do with stdio
- if not daemon:
- # do nothing, this process is open until the spawns close anyway
- re_out = None
- re_err = None
- else:
- re_err = subprocess.STDOUT
- if wait:
- # we're going to need to block on this...
- re_out = subprocess.PIPE
- else:
- re_out = open(os.devnull, 'w+b')
-
- # Popen:创建新的Popen实例;
- proc = subprocess.Popen(args, stdout=re_out, stderr=re_err)
-
- # 转换conf_file到相应的pid_file;
- # 返回conf_file相应的pid_file;
- pid_file = self.get_pid_file_name(conf_file)
-
- # 写proc.pid内容到pid_file;
- write_file(pid_file, proc.pid)
-
- self.procs.append(proc)
-
- return proc.pid这个方法完成的是调用方法Popen实现把一个服务放到一个子进程中去运行;
复制代码
来具体看代码:
- args = [self.cmd, conf_file]
-
- if once:
- args.append('once')
-
- if not daemon:
- # ask the server to log to console
- args.append('verbose')这部分代码完成的是把cmd和配置文件conf_file路径以及其他一些参数整合到一起,组成命令参数args,将会用于在后面传入到方法Popen中,实现在子进程中运行args中指定的服务;
复制代码
调试示例:
- args = ['swift-proxy-server', '/etc/swift/proxy-server.conf']
复制代码
即指定了要运行的服务和它的配置文件;
继续看代码:
- # figure out what we're going to do with stdio
- if not daemon:
- # do nothing, this process is open until the spawns close anyway
- re_out = None
- re_err = None
- else:
- re_err = subprocess.STDOUT
- if wait:
- # we're going to need to block on this...
- re_out = subprocess.PIPE
- else:
- re_out = open(os.devnull, 'w+b')这段代码实现的是明确一些参数用于告诉子进程要做什么,但是具体的参数含义还没有研究;
复制代码
来看最关键的一条语句:
- # Popen:创建新的Popen实例;
- proc = subprocess.Popen(args, stdout=re_out, stderr=re_err)这里调用了Popen方法实现了把指定的服务放到一个子进程中来运行;
复制代码
这里传入了参数args指定了要运行的服务和相应服务的配置文件,这是比较关键的地方;
这个方法中实际上分别调用了/usr/bin/swift-proxy-server、/usr/bin/swift-container-server、/usr/bin/swift-account-server、/usr/bin/swift-object-server等程序中的main方法,实现了对指定服务的启动运行;我们来深入跟踪一下args这个参数,来看看是如何实现调用这几个服务代码的:
- class Popen(object):
- def __init__(self, args, bufsize=0, executable=None,
- stdin=None, stdout=None, stderr=None,
- preexec_fn=None, close_fds=False, shell=False,
- cwd=None, env=None, universal_newlines=False,
- startupinfo=None, creationflags=0):
- """Create new Popen instance."""
- ......
- self._execute_child(args, executable, preexec_fn, close_fds,
- cwd, env, universal_newlines,
- startupinfo, creationflags, shell,
- p2cread, p2cwrite,
- c2pread, c2pwrite,
- errread, errwrite)def _execute_child(self, args, executable, preexec_fn, close_fds,
- cwd, env, universal_newlines,
- startupinfo, creationflags, shell,
- p2cread, p2cwrite,
- c2pread, c2pwrite,
- errread, errwrite):
- """Execute program (POSIX version)"""
- if isinstance(args, types.StringTypes):
- args = [args]
- else:
- args = list(args)
- if shell:
- args = ["/bin/sh", "-c"] + args
- if executable:
- args[0] = executable
- if executable is None:
- executable = args[0]
- ......
- try:
- try:
-
- ......
- if self.pid == 0:
- try:
-
- ......
-
- if env is None:
- os.execvp(executable, args)
- else:
- os.execvpe(executable, args, env)
- ......调试运行示例:
- executable = swift-proxy-server
- args = ['swift-proxy-server', '/etc/swift/proxy-server.conf']
- env = None
- def execvp(file, args):
- _execvpe(file, args)def _execvpe(file, args, env=None):
- if env is not None:
- func = execve
- argrest = (args, env)
- else:
- func = execv
- argrest = (args,)
- env = environ
- head, tail = path.split(file)
- if head:
- func(file, *argrest)
- return
- if 'PATH' in env:
- envpath = env['PATH']
- else:
- envpath = defpath
- PATH = envpath.split(pathsep)
- saved_exc = None
- saved_tb = None
- for dir in PATH:
- fullname = path.join(dir, file)
- try:
- func(fullname, *argrest)
-
- ......
- ......调试运行示例:
- argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
- fullname = /usr/local/sbin/swift-proxy-server
- argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
- fullname = /usr/local/bin/swift-proxy-server
- argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
- fullname = /usr/sbin/swift-proxy-server
- argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
- fullname = /usr/bin/swift-proxy-server
- argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
复制代码
我们可以看到,程序实现了依次查找/usr/local/sbin/、 /usr/local/bin/、/usr/sbin/和/usr/bin/等路径,查找swift-proxy-server文件,并执行其中的main方法;
我会在下一篇博客中具体分析,swift-proxy-server中的main方法是怎样实现运行proxy服务的;
继续来看方法spawn的代码:
- # 转换conf_file到相应的pid_file;
- # 返回conf_file相应的pid_file;
- pid_file = self.get_pid_file_name(conf_file)
-
- # 写proc.pid内容到pid_file;
- write_file(pid_file, proc.pid)
-
- self.procs.append(proc)
复制代码
return proc.pid这段代码主要做了一下几件事:
(1)通过conf_file获取相应的pid_file;
(2)把新启动的子进程的pid写进到文件pid_file之中;
(3)把新启动的子进程的对象加入到procs之中;
(4)返回新启动的子进程的pid值;
到这里方法spawn分析完成,我们大体可以了解了系统是怎样启动一个子进程来运行指定的服务的;
我们回到方法lauch,方法最后返回了所启动的一系列子进程的集合pids;
我们回到方法start,方法的最后根据参数设定,来决定执行方法interact或者方法wait,来等待子进程的运行结束;
最后方法start返回子进程的运行状态给swift-init中的方法main,即解析完成方法main中的语句:
status = manager.run_command(command, **options.__dict__)
至此,整个swift服务的启动过程我们分析完成,我将会在下一篇博客中具体分析swift-proxy-server中的main方法是如何实现运行proxy服务的。
博文中不免有不正确的地方,欢迎朋友们不吝批评指正,谢谢大家了!
相关内容:
OpenStack Swift源码分析(1)----swift服务启动源码分析之一
OpenStack Swift源码分析(2)----swift服务启动源码分析之二
OpenStack Swift源码分析(3)----swift服务启动源码分析之三
|