python-高级编程-03

 

【多进程与多线程】

调度 : 在传统计算机操作系统中 cpu的调度的基本单位是进程,随着线程的引入,线程变成操作系统的最小调度单位

           而进程是作为资源的拥有单位。

并行:由于线程的引入 原先一个进程只能有一个并发 现在一个进程可以有多个线程并行执行,

     早起的httpserver 都是通过线程来解决服务器的并发 比起之前用fork子进程来处理并发效率有很大的提升。

     这一切得益于线程可以用进程更低的代价实现并发。

共享 :一般linux线程会让线程继承或共享如下资源

         。进程的共有数据内存 

         。 进程所打开的文件描述符

         。 信号的处理器

         。 进程用户的ID和进程组ID 

隔离:

            。线程ID ,在linux中 线程和进程共享ID空间

    。寄存器的值

    。线程的栈

    。 优先级  linux的系统设计使得线程和进程除了在某些资源的共享和隔离有差异外,几乎是一视同仁的,他们可以有不同观点priority

我们在选择多进程还是多线程的时候需要根据业务场景使用。他们特性就是共享和隔离的区别。

【Linux的进程】

       Linux在linus设计的时候的定位就是一个多任务的操作系统,从linux出的第一个版本的时候 就有了 进程的概念,

  线程的产生是为了解决并发问题,线程的定位也就是更小更轻的进程

  一些问题:

     为什么不能一味的开线程解决并发问题?

    #线程的上下文切换所带来的消耗,开的线程越多,在上下文切换过程中消耗就越大,核心就是内存根不上cpu的速度。    

  上下文切换: 在cpu还是单核的时候,计算机操作系统就已经实现了多任务系统,但是你要知道 单核的cpu在同一时间段内

           只能执行某一个进程的某一个指令,为了达到多任务的执行效果,linux把cpu的时间切成大小不到等的时间

          片,通过内核调度算法,让进程一个个上去跑,由于切换时间片的时间非常短,在我们人类看来 计算机在同时执行多个程序。

             那么这个是如果实现的呢,如果程序到了时间片结束之后还没有完成它的工作,那么操作系统会把这个程序,以及其依赖的数据

          都保存在内存里,然后回到进程的队列里。保存现场是需要代价的,这将极大的影响cpu的分支预测,影响系统性能,

          所以频繁的上下文切换,是我们及其避免的。

【协程】

    协程就是用户自己在进程中控制多任务的栈,尽可能的不让进程由于外部中断或者IO的等待丧失CPU调度的时间片,从而在进程内部实现并发。

 

【内存与守护进程】    

程序运行时的内存,也就是我们在用户状态能看到的内存地址,都不是物理内存的地址,现代操作系统都会在物理内存上做一层,内存映射,每个

进程内的内存空间都是独立的

 

守护进程的特点:

  1 后台运行,也不占用conscle的前面 也就是bash里运行程序后面加个&

  2 成为  process group leader 守护进程的父进程是init的那个进程

  3 成为 session leader 一个ssh登陆会启动一个bash bash会fork很多子进程,这些进程轮流接受tty输出,这都是一个session session leader就是一队进程的父进程

  4 fork 一次到两次 因为linux父进程只对子进程负责,fork两次可以保证不影响正在执行的程序,直接交给init

  5 chdir到/ 防止占用别的路径的 working dir的id 导致block不能

  6 需要重置umask 方式后续子进程继承非默认umask造成不可控问题

  7 处理标准输入输出 错误输出(0,1,2) 重定向stdout stderr stdin 防止tty中断后的broken pipe信号

      8 日志 输入重定向后,需要有办法反映内部情况

 

--关于僵尸进程--父进程派生出子进程 如果子进程挂了 但是一般进程 父进程会拿着子进程的pid 调用wait,如果父进程对子进程没有处理,这个这个时候就会变成僵尸进程

如果父进程也挂了init会回收僵尸进程 而僵尸进程存的就是僵尸进程退出的退出码。

 

 【用python写一个守护进程】

   

#!/usr/bin/env python
#coding=utf-8
import os,sys
import time

'''

使用python写个守护进程
'''

def daemonzie(stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'):
    try:
        pid = os.fork()
        if pid >0:
            sys.exit(0)

    except OSError ,e:
        sys.stderr.write("fork #2 failed (%d) %s\n "%(e.errno,e.strerror))
        sys.exit(1)

    os.chdir('/')# chdir到/ 防止占用别的路径的 working dir的id 导致block不能
    os.umask(0)#需要重置umask 方式后续子进程继承非默认umask造成不可控问题
    os.setsid()#成为  process group leader 守护进程的父进程是init的那个进程

    try:
        pid = os.fork()
        if pid >0:
            sys.exit(0)

    except OSError,e:
        sys.stderr.write("fork #2 failed (%d) %s\n " % (e.errno, e.strerror))
        sys.exit(1)

    for i in sys.stdout,sys.stderr:i.flush()
    si = open(stdin,'r+')
    so = open(stdout,'a+')
    se = open(stderr,'a+',0)
    os.dup2(si.fileno(),sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdin.fileno())
    os.dup2(se.fileno(), sys.stdin.fileno())

def main():
    import time
    sys.stdout.write('Daemon started with pid %s\n'%os.getpid())
    sys.stdout.write('Daemon stdout output\n')
    sys.stderr.write('Daemon stderr output\n')
    c = 0
    while 1:
        sys.stdout.write('%d:%s\n'%(c,time.ctime()))
        sys.stdout.flush()
        c + c+1
        time.sleep(1)


if __name__ == '__main__':
    daemonzie('/dev/null','/tmp/daemon_stdout.log','/tmp/daemon_error.log')
    main()

 说白了 我们写一个 while 1 :print 'xxxx' 这种其实就是守护进程的基础 我们需要对一些东西进行处理后 才能形成一个正常的守护进程

【多线程实例】

1>

#!/usr/bin/env python
#coding:utf-8

import thread

def f(name):
    #定义线程函数
    print 'this is '+name

if __name__ == '__maim__':
    thread.start_new_thread(f,("thread1",))
    while 1:
        pass

  

2>

#/usr/bin/env python
#coding:utf-8

import  threading

class Th(threading.Thread):
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.t_name = name
    def run(self):
        print "this is " + self.t_name

if __name__ == '__main__':
    thread1= Th("Thread1")
    thread1.start()

  

threading.Thread 类的可继承函数
getName() 获得线程对象名称
setName() 设置线程对象名称

join() 等待调用的线程件数后再运行的命令

setDaemin(bool)阻塞模式
  True 父线程不等待子线程的结束
  False 等待默认为等待
isDaemon() 判断子线程是否和父线程一起结束 即setDaemon() 设置的值
isAlive()判断线程是否在运行


import threading
import time

class my_therad(threading.Thread):
    def __init__(self,thread_name):
        threading.Thread.__init__(self)
        self.setName=(thread_name)

    def run(self):
        print 'this is thread'+ self.getName()
        for i in range(5):
            time.sleep(1)
            print(str(i))
        print self.getName()+'is over'

if __name__ == '__main__':
    thread1 = my_therad('T1')
    thread1.start()
    #thread1.join()
    print 'main thread is over'

 这里如果加了join()

this is threadThread-1
0
1
2
3
4
Thread-1is over
main thread is over

 如果没加

this is threadThread-1
 main thread is over
0
1
2
3
4
Thread-1is over

 加了join 主线程会等待子线程结束返回之后才会执行

-------------------------------------------------------------------

import threading
import time

class my_therad(threading.Thread):
    def __init__(self,thread_name):
        threading.Thread.__init__(self)
        self.setName=(thread_name)

    def run(self):
        print 'this is thread'+ self.getName()
        for i in range(5):
            time.sleep(1)
            print(str(i))
        print self.getName()+'is over'

if __name__ == '__main__':
    thread1 = my_therad('T1')
    thread1.setDaemon(True)
    thread1.start()
    #thread1.setDaemon(True)
    #thread1.join()
    print 'main thread is over'

  如果setDaemon 在start之前那么 主线程不会等待子线程,直接结束了

输出:

起多个子线程

if __name__ == '__main__':
    for i in range(3):
        t = my_therad(str(i))
        t.start()
    print 'main thread is over'

  【线程锁】

import  threading

import time

class Th(threading.Thread):
    def __init__(self,thread_name):
        threading.Thread.__init__(self)
        self.setName(thread_name)



    def run(self):
        threadLock.acquire()
        print "this is thread "+self.getName()
        for i in range(3):
            time.sleep(1)
            print str(i)
        print self.getName()+'  is over'
        threadLock.release()


if __name__ == '__main__':
    threadLock = threading.Lock()
    thread1 = Th('Thread_1')
    thread2 = Th('Thread_2')
    thread1.start()
    thread2.start()

  如果加上锁输出就是这样

this is thread Thread_1
0
1
2
Thread_1  is over
this is thread Thread_2
0
1
2
Thread_2  is over

  

如果不加

this is thread Thread_1
 this is thread Thread_2
0
 0
1
 1
2
 Thread_2  is over
2
Thread_1  is over

  在thread中 优先会让之前拿到锁的人去拿锁 这样可以保证cpu分支预测的成功率

 

 =======================================================================================================

#!/usr/bin/env python
#coding:utf-8
#####################
# time:2017-08-11   #
#####################
'''
this class is Daemon class

'''

import os,sys,time,atexit

from signal import SIGTERM

class Daemon():
    def __init__(self,pidfile = 'nbmon.pid',stdin='/dev/null',stdout='nbmon.log',stderr ='nbmon.log'):
        self.stdin = stdin
        self.stdout =stdout
        self.stderr = stderr
        self.pidfile = pidfile

    def daeminize(self):
        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)
        except OSError ,e:
            sys.stderr.write("fork #1 failed:%d(%s)"(e.errno,e.strerror))
            sys.exit(1)

        os.chdir('/')
        os.setsid()
        os.umask(0)

        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)
        except OSError ,e:
            sys.stderr.write("fork #2 failed:%d(%s)"(e.errno,e.strerror))
            sys.exit(1)

        sys.stdout.flush()
        sys.stderr.flush()
        si= file(self.stdin,'r')
        so= file(self.stdout,'a+')
        se = file(self.stderr,'a+',0)
        os.dup2(si.fileno(),sys.stdin.fileno())
        os.dup2(si.fileno(),sys.stdout.fileno())
        os.dup2(si.fileno(),sys.stderr.fileno())
        atexit.regoster(self.delpid)
        pid = str(os.getpid())
        file(self.pidfile,'w+'.write("%s\n"%pid))

    def delpid(self):
        os.remove(self.pidfile)
    
    def start(self):
        try:
            pf = file(self.pidfile,'r')
            pid = int(pf.read().strip())
            pf.close()
        except IOError,e:
            pid = None
         

        if pid :
            message = "pidfile %s alreadly exit,Daemon is running\n"
            sys.stderr.write(message % self.pidfile)
            sys.exit(1)

        self.daemonize()
        self.run()

    def stop(self):
        try:
            pf = file(self.pidfile,'r')
            pid = int(pf.read().strip())
            pf.close

        except IOError:
            pid = None

        if not pid:
            message = "pidfile %s does not exitst,Daemon is running\n"
            sys.stderr.write(message % self.pidfile)
            return
        try:
            while 1:
                os.kill(pid,STGTERM)
                time.sleep(0.1)
        except OSError,err:
            err = str(err)
            if err.find("No such process") > 0 :
                if os.path.exists(self.pidfile):
                    os.remove(self.pidfile)
            else:
                print srt(err)
                sys.exit(1)
    def restart(self):
        self.stop()
        self.start()
    def run(self):
        pass
#!/usr/bin/env  python
#coding:utf-8

from daemon import Daemon
import socket
import time

html= """HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nConnection: close\r\nContent-Length: """
html404 =  """HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 13\r\n\r\n<h1>404 </h1>"""

class agentD(Daemon):
    def run(self):
        listen_fd = socket.socket(socket.AF_INET,socket.SOCK_STREAM, 0)
        listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        listen_fd.bind(("0.0.0.0",9085))
        listen_fd.listen(10)
        while True:
            conn,addr  = listen_fd.accept()
            print "coming",conn,addr
            read_data = conn.recv(10000)
            try:
                pic_name = read_data.split(" ")[1][1:]

                print pic_name,'*********'

                with file(pic_name) as f:
                    pic_content = f.read()
                    lengths = len(pic_content)
                    print lengths,"####"
                    html_resp = html
                    html_resp += "%d\r\n\r\n" % (lengths)
                    print html_resp
                    html_resp += pic_content
            except:
                print "404 occur"
                html_resp = html404
            while len(html_resp)>0:
                sent_cnt = conn.send(html_resp)
                print "sent:",sent_cnt
                html_resp = html_resp[sent_cnt:]

            conn.close()

if __name__ == '__main__':
    agentd = agentD(pidfile = 'agent.pid',stdout='agent.log',stderr ='agent.log')
    agentd.run()

  










 

posted @ 2017-07-29 15:47  nerdlerss  阅读(293)  评论(0编辑  收藏  举报