精华内容
下载资源
问答
  • 进程

    万次阅读 2021-05-25 14:29:00
    文章目录进程一.什么是多任务二、实现多任务的方式:示例1 一般情况下执行唱歌、跳舞示例2 单线程执行函数示例3 多线程执行函数示例4多线程执行唱歌、跳舞1示例5多线程执行唱歌、跳舞2三、什么是进程1.什么是进程?2...

    进程

    一.什么是多任务

    现代的操作系统(Windows,Mac OS X,Linux,UNIX等)都支持“多任务”。
    什么叫做多任务:操作系统同时运行多个任务

    • 单核CPU实现多任务原理:操作系统轮流让各个任务交替执行,QQ执行2us,切换到微信,再执行2us,再切换到陌陌,执行2us······。表面上是每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉就像所有任务都在同时执行一样。

    • 多核CPU实现多任务原理:真正的并行执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

    并发:看上去一起执行,任务数多于CPU核心数
    并行:真正一起执行,任务数小于等于CPU核心数

    二、实现多任务的方式:

    1.多进程模式(使用较多)
    2.多线程模式(使用最多)
    3.协程模式 (使用较少)
    4.多进程+多线程模式 (容易出错)

    生活中与很多场景是同时进行的,比如开车的时候手和脚是同时来控制车。还有一些歌手边唱歌边跳舞。
    我们可以理解为我们的大脑控制着我们的身体进行不同的动作。大脑就是cpu,身体的所有支配动作用到的部位为我们的资源。
    试想下如果我们的大脑只能控制一个动作完成后才能控制下一个动作,也就是说我们只能先唱完歌再去跳舞,这是不是很尴尬呢?

    示例1 一般情况下执行唱歌、跳舞

    from time import sleep 
    def sing():
        for i in range(3):
            print('我正在唱歌。。。%d'%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print('我正在跳舞。。。%d'%i)
            sleep(1)
    if __name__ == '__main__':
        sing() # 先唱歌
        dance() # 再跳舞
    '''
    我正在唱歌。。。0
    我正在唱歌。。。1
    我正在唱歌。。。2
    我正在跳舞。。。0
    我正在跳舞。。。1
    我正在跳舞。。。2'''
    
    

    示例2 单线程执行函数

    from time import sleep
    # 单线程执行
    def saySorry():
        print("亲爱的,圣诞节快乐!")
        sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            saySorry()
    """
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    """
    
    

    示例3 多线程执行函数

    import threading
    from time import sleep
    # 多线程执行
    def saySorry():
        print("亲爱的,圣诞节快乐!")
        sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            t = threading.Thread(target=saySorry)
            t.start()  # 启动线程,即让线程开始执行
    """
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    """
    
    

    示例4多线程执行唱歌、跳舞1

    import threading
    from time import sleep,ctime
    # 多线程执行唱歌跳舞的多任务。
    def sing():
        for i in range(3):
            print("正在唱歌...%d"%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d"%i)
            sleep(1)
    
    if __name__ == '__main__':
        print('---开始---:%s'%ctime())
    
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
    
        t1.start()
        t2.start()
    
        sleep(5)  # 屏蔽此行代码,试试看,程序是否会立马结束?
        print('---结束---:%s'%ctime())
    
    '''
    ---开始---:Tue May 25 11:26:19 2021
    正在唱歌...0
    正在跳舞...0
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2
    ---结束---:Tue May 25 11:26:24 2021
    '''
    
    

    示例5多线程执行唱歌、跳舞2

    import threading
    from time import sleep,ctime
    # 多线程执行唱歌跳舞的多任务。
    def sing():
        for i in range(3):
            print("正在唱歌...%d"%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d"%i)
            sleep(1)
    
    if __name__ == '__main__':
        print('---开始---:%s'%ctime())
    
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
    
        t1.start()
        t2.start()
    
        # sleep(5) # 屏蔽此行代码,试试看,程序是否会立马结束?
        print('---结束---:%s'%ctime())
    
    '''---开始---:Tue May 25 11:19:25 2021
    正在唱歌...0
    正在跳舞...0---结束---:Tue May 25 11:19:25 2021
    
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2'''
    
    

    三、什么是进程

    1.什么是进程?

    对于操作系统而言,一个任务就是一个进程,一个应用就是一个进程。
    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
    进程是系统中程序执行和资源分配的基本单位,每个进程都有自己的数据段、代码段、和堆栈段。
    进程是一个具有一定独立功能的程序,一个实体,每一个进程都有它自己的地址空间。

    2.进程的状态

    进程执行时的间断性,决定了进程可能具有多种状态,事实上,运行中的进程具有以下三种基本状态。
    (1)就绪状态(Ready)
    (2)运行状态(Running)
    (3)阻塞状态(Blocked)

    3.单任务现象

    # 单任务现象
    from time import sleep
    
    def run():
        while True:
            print("haiyan is my wife")
            sleep(2)
    
    if __name__ == "__main__":
        while True:
            print("yangyu is a good man")
            sleep(1)
    
        run()    # 执行不了.因为上面的循环没有结束。
    '''
    执行结果:不停打印
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    ...
    '''
    
    

    4.启动进程实现多任务

    # 启动进程实现多任务
    """
    multiprocessing 库
    跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象
    """
    from multiprocessing import Process
    import os
    from time import sleep
    
    
    # 子进程需要执行的代码
    def run(str1):
        while True:
            # os.getpid()获取当前进程的id号
            # os.getppid()获取当前父进程的id号
            print("yangyu is a %s man--%s--%s" % (str1, os.getpid(), os.getppid()))
            sleep(1.2)
    
    
    if __name__ == "__main__":
        print("主(父)进程启动-%s" % (os.getpid()))
        # 创建子进程
        # target说明进程执行的任务
        p = Process(target=run, args=("nice",))
        # 启动进程
        p.start()
    
        while True:
            print("yangyu is a good man")
            sleep(1)
    '''
    主(父)进程启动-2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372'''
    
    

    5.父子进程的先后顺序

    # 父子进程的先后顺序
    from multiprocessing import Process
    from time import sleep
    
    def run(str1,str2):
        print("子进程启动+%s"%str1)
        sleep(3)
        print("子进程结束+%s"%str2)
    
    if __name__ == "__main__":
        print("父进程启动")
    
        p = Process(target=run, args=("start","end"))
        p.start()
    
        # 父进程的结束不能影响子进程,让父进程等待子进程的结束再执行父进程
        p.join()
        print("父进程结束")
    '''
    父进程启动
    子进程启动+start
    子进程结束+end
    父进程结束
    '''
    
    

    6.多个进程不能共享全局变量

    # 多个进程不能共享全局变量
    # 在子进程中修改全局变量对父进程中的全局变量没有影响
    # 在创建子进程时对全局变量做了一个备份,父进程中的子进程中的num是完全不同的两个变量
    from multiprocessing import Process
    num = 100
    def run():
        print("子进程开始")
        global num  # num = 100
        num += 1
        print(num)
        print("子进程结束")
    
    if __name__ == "__main__":
        print("父进程开始")
        p = Process(target=run)
        p.start()
        p.join()
        print("父进程结束--%d"%num)
    '''
    父进程开始
    子进程开始
    101
    子进程结束
    父进程结束--100
    '''
    
    

    7.启动多个子进程

    # 启动多个子进程
    from multiprocessing import Pool
    import os,time
    def run(name):
        print("子进程%d启动--%s" % (name, os.getpid()))
        start = time.time()
        time.sleep(3)
        end = time.time()
        print("子进程%d结束--%s--耗时%.2f" % (name, os.getpid(), end-start))
    
    if __name__ == "__main__":
        t1 = time.time()
        print("父进程启动")
        # 创建多个进程
        # 进程池
        # 表示可以同时执行的进程数量
        # Pool默认大小是cpu核心线程数,我的笔记本电脑是2核4线程(这里的线程就同时执行的进程),Pool()默认为4,默认同时执行4进程,总耗时为18.2s;改为2,总耗时为32.5s;改为8,总耗时14.1s;改为20,总耗时20.35s。所有一般就用核心数*线程数(2*4=8),执行速度最快。
        pp = Pool(8)
        for i in range(20):
            # 创建进程,放入进程池同意管理
            pp.apply_async(run, args=(i,))
        # 在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了
        pp.close()
        # 进程池对象调用join,会等待进程池中所有的子进程结束完毕再去执行父进程
        pp.join()
        print("父进程结束,总耗时为%s"%(time.time()-t1))
    '''
    父进程启动
    子进程0启动--9048
    子进程1启动--3648
    子进程2启动--7180
    子进程3启动--7888
    子进程4启动--8228
    子进程5启动--8664
    子进程6启动--8688
    子进程7启动--9432
    子进程0结束--9048--耗时3.01
    子进程8启动--9048
    子进程1结束--3648--耗时3.01
    子进程9启动--3648
    子进程2结束--7180--耗时3.01
    子进程10启动--7180
    子进程3结束--7888--耗时3.01
    子进程11启动--7888
    子进程4结束--8228--耗时3.01
    子进程12启动--8228
    子进程5结束--8664--耗时3.01
    子进程13启动--8664
    子进程6结束--8688--耗时3.01
    子进程14启动--8688
    子进程7结束--9432--耗时3.01
    子进程15启动--9432
    子进程8结束--9048--耗时3.07
    子进程16启动--9048
    子进程9结束--3648--耗时3.01
    子进程17启动--3648
    子进程10结束--7180--耗时3.01
    子进程18启动--7180
    子进程11结束--7888--耗时3.01
    子进程19启动--7888
    子进程12结束--8228--耗时3.01
    子进程13结束--8664--耗时3.01
    子进程14结束--8688--耗时3.01
    子进程15结束--9432--耗时3.01
    子进程16结束--9048--耗时3.01
    子进程17结束--3648--耗时3.01
    子进程18结束--7180--耗时3.04
    子进程19结束--7888--耗时3.01
    父进程结束,总耗时为14.196025133132935
    '''
    
    

    8.多进程文件拷贝

    1.普通文件拷贝

    # 实现文件的拷贝
    import os, time
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"F:\PycharmProjects\Project\进程\file1\1905热门电影图片"
    toPath = r"F:\PycharmProjects\Project\进程\file2"
    
    # 读取path下的所有文件
    fileList = os.listdir(path)
    
    # 启动for循环去处理每个文件
    start = time.time()
    for fileName in fileList:
        copyFile(os.path.join(path,fileName), os.path.join(toPath, fileName))
    end = time.time()
    print("总耗时:%0.2f" % (end-start))  # 总耗时:14.68
    

    2.多进程文件拷贝

    import os, time
    from multiprocessing import Pool
    # 实现文件的拷贝
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"F:\PycharmProjects\Project\进程\file1\1905热门电影图片"
    toPath = r"F:\PycharmProjects\Project\进程\file2"
    
    if __name__ == "__main__":
        # 读取path下的所有文件
        fileList = os.listdir(path)
        start = time.time()
        pp = Pool(4)
        for fileName in fileList:
            pp.apply_async(copyFile, args=(os.path.join(path, fileName), os.path.join(toPath, fileName)))
        pp.close()
        pp.join()
        end = time.time()
        print("总耗时:%0.2f" % (end - start))  # 总耗时:11.40
    

    9.封装进程对象

    1.创建yangyuProcess.py文件

    from multiprocessing import Process
    import os, time
    
    class YangyuProcess(Process):
        def __init__(self, name):
            Process.__init__(self)
            self.name = name
    
        def run(self):
            print("子进程(%s-%s)启动" % (self.name, os.getpid()))
    
            # 子进程的功能
            time.sleep(3)
    
            print("子进程(%s-%s)结束" % (self.name, os.getpid()))
    

    2.from yangyuProcess import YangyuProcess

    from yangyuProcess import YangyuProcess
    
    if __name__ == "__main__":
        print("父进程启动")
        # 创建子进程
        p = YangyuProcess("test")
        # 自动调用p进程对象的run方法
        p.start()
        p.join()
        print("父进程结束")
    '''
    打印结果:
    父进程启动
    子进程(test-3476)启动
    子进程(test-3476)结束
    父进程结束
    '''
    """
    这样写的好处:子进程的方法不用和父进程的方法写在一起,让主程序结构更清晰。
    """
    

    10.进程间的通信

    1.示例1

    from multiprocessing import Process, Queue
    import os,time
    
    def write(q):
        print("启动写子进程%s"%(os.getpid()))
        for chr in ["A","B","C","D"]:
            q.put(chr)
            time.sleep(1)
        print("结束写子进程%s"%(os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while True:
            value = q.get(True)
            print("value=" + value)
        print("结束读子进程%s" % (os.getpid()))
    
    
    if __name__ == "__main__":
        # 父进程创建队列,并传给子进程
        q = Queue()
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
    
        pw.join()
        pr.terminate()  # pr进程里是个死循环,无法等待其结束,只能强制结束
    
        print("父进程结束")
    
    '''
    启动写子进程7752
    启动读子进程7264
    value=A
    value=B
    value=C
    value=D
    结束写子进程7752
    父进程结束'''
    
    

    2.示例2

    from multiprocessing import Process, Queue
    import os,time
    
    def write(q):
        print("启动写子进程%s"%(os.getpid()))
        for chr in ["A","B","C","D"]:
            q.put(chr)
            print(chr)
        print("结束写子进程%s"%(os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while not q.empty():
            value = q.get(True)
            print("value=" + value)
            time.sleep(1)
        print("结束读子进程%s" % (os.getpid()))
    
    
    if __name__ == "__main__":
        print("父进程开始")
        # 父进程创建队列,并传给子进程
        q = Queue(4)
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
        pw.join()
        pr.join()
        print("父进程结束")
    
    '''
    父进程开始
    启动写子进程8228
    A
    B
    C
    D
    结束写子进程8228
    启动读子进程7472
    value=A
    value=B
    value=C
    value=D
    结束读子进程7472
    父进程结束
    '''
    
    展开全文
  • 进程和线程的区别(超详细)

    万次阅读 多人点赞 2019-10-03 21:57:46
    进程和线程 进程 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。 线程 进程中的一个执行任务(控制单元),负责...

    进程和线程

    进程

    一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。

    任务管理器

    线程

    进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。

    与进程不同的是同类的多个线程共享进程的方法区资源,但每个线程有自己的程序计数器虚拟机栈本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

    Java 程序天生就是多线程程序,我们可以通过 JMX 来看一下一个普通的 Java 程序有哪些线程,代码如下。

    public class MultiThread {
    	public static void main(String[] args) {
    		// 获取 Java 线程管理 MXBean
    		ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    		// 不需要获取同步的 monitor 和 synchronizer 信息,仅获取线程和线程堆栈信息
    		ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
    		// 遍历线程信息,仅打印线程 ID 和线程名称信息
    		for (ThreadInfo threadInfo : threadInfos) {
    			System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
    		}
    	}
    }
    

    上述程序输出如下(输出内容可能不同,不用太纠结下面每个线程的作用,只用知道 main 线程执行 main 方法即可):

    [6] Monitor Ctrl-Break //监听线程转储或“线程堆栈跟踪”的线程
    [5] Attach Listener //负责接收到外部的命令,而对该命令进行执行的并且把结果返回给发送者
    [4] Signal Dispatcher // 分发处理给 JVM 信号的线程
    [3] Finalizer //在垃圾收集前,调用对象 finalize 方法的线程
    [2] Reference Handler //用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收的线程
    [1] main //main 线程,程序入口
    

    从上面的输出内容可以看出:一个 Java 程序的运行是 main 线程和多个其他线程同时运行

    进程与线程的区别总结

    线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

    根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

    资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

    包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

    内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

    影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

    执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

    从 JVM 角度说进程和线程之间的关系(重要)

    图解进程和线程的关系

    下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。

    在这里插入图片描述

    从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器虚拟机栈本地方法栈

    程序计数器为什么是私有的?

    程序计数器主要有下面两个作用:

    1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
    2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

    需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。

    所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置

    虚拟机栈和本地方法栈为什么是私有的?

    • 虚拟机栈:每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
    • 本地方法栈:和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

    所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。

    一句话简单了解堆和方法区

    堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象 (所有对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    多进程和多线程区别

    多进程:操作系统中同时运行的多个程序

    多线程:在同一个进程中同时运行的多个任务

    举个例子,多线程下载软件,可以同时运行多个线程,但是通过程序运行的结果发现,每一次结果都不一致。 因为多线程存在一个特性:随机性。造成的原因:CPU在瞬间不断切换去处理各个线程而导致的,可以理解成多个线程在抢CPU资源。

    多线程提高CPU使用率

    多线程

    多线程并不能提高运行速度,但可以提高运行效率,让CPU的使用率更高。但是如果多线程有安全问题或出现频繁的上下文切换时,运算速度可能反而更低。

    Java中的多线程

    Java程序的进程里有几个线程:主线程,垃圾回收线程(后台线程)等

    在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。

    Java支持多线程,当Java程序执行main方法的时候,就是在执行一个名字叫做main的线程,可以在main方法执行时,开启多个线程A,B,C,多个线程 main,A,B,C同时执行,相互抢夺CPU,Thread类是java.lang包下的一个常用类,每一个Thread类的对象,就代表一个处于某种状态的线程

    展开全文
  • Android跨进程通信:图文详解 Binder机制 原理

    万次阅读 多人点赞 2017-06-22 10:31:24
    如果你接触过 跨进程通信 (IPC),那么你对Binder一定不陌生 虽然 网上有很多介绍 Binder的文章,可是存在一些问题:浅显的讨论Binder机制 或 一味讲解 Binder源码、逻辑不清楚,最终导致的是读者们还是无法形成一...

    前言

    • 如果你接触过 跨进程通信 (IPC),那么你对Binder一定不陌生
    • 虽然 网上有很多介绍 Binder的文章,可是存在一些问题:浅显的讨论Binder机制 或 一味讲解 Binder源码、逻辑不清楚,最终导致的是读者们还是无法形成一个完整的Binder概念
    • 本文采用 清晰的图文讲解方式,按照 大角度 -> 小角度 去分析Binder,即:
      1. 先从 机制、模型的角度 去分析 整个Binder跨进程通信机制的模型
      2. 再 从源码实现角度,分析 BinderAndroid中的具体实现

    从而全方位地介绍 Binder,希望你们会喜欢。

    请尽量在PC端而不要在移动端看,否则图片可能看不清。


    目录

    目录


    1. Binder到底是什么?

    • 中文即 粘合剂,意思为粘合了两个不同的进程

    • 网上有很多对Binder的定义,但都说不清楚:Binder是跨进程通信方式、它实现了IBinder接口,是连接 ServiceManager的桥梁blabla,估计大家都看晕了,没法很好的理解

    • 我认为:对于Binder的定义,在不同场景下其定义不同

    定义

    在本文的讲解中,按照 大角度 -> 小角度 去分析Binder,即:

    • 先从 机制、模型的角度 去分析 整个Binder跨进程通信机制的模型

    其中,会详细分析模型组成中的 Binder驱动

    • 再 从源码实现角度,分析 BinderAndroid中的具体实现

    从而全方位地介绍 Binder,希望你们会喜欢。


    2. 知识储备

    在讲解Binder前,我们先了解一些Linux的基础知识

    2.1 进程空间划分

    • 一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内核 隔离开来
    • 二者区别:
      1. 进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
      2. 进程间,内核空间的数据可共享,所以内核空间 = 可共享空间

    所有进程共用1个内核空间

    • 进程内 用户空间 & 内核空间 进行交互 需通过 系统调用,主要通过函数:
    1. copy_from_user():将用户空间的数据拷贝到内核空间
    2. copy_to_user():将内核空间的数据拷贝到用户空间

    示意图

    2.2 进程隔离 & 跨进程通信( IPC )

    • 进程隔离
      为了保证 安全性 & 独立性,一个进程 不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的

    • 跨进程通信( IPC
      即进程间需进行数据交互、通信

    • 跨进程通信的基本原理

    示意图

    a. 而Binder的作用则是:连接 两个进程,实现了mmap()系统调用,主要负责 创建数据接收的缓存空间 & 管理数据接收缓存
    b. 注:传统的跨进程通信需拷贝数据2次,但Binder机制只需1次,主要是使用到了内存映射,具体下面会详细说明

    2.5 内存映射

    具体请看文章:操作系统:图文详解 内存映射


    3. Binder 跨进程通信机制 模型

    3.1 模型原理图

    Binder 跨进程通信机制 模型 基于 Client - Server 模式
    示意图

    3.2 模型组成角色说明

    示意图

    此处重点讲解 Binder驱动的作用 & 原理:

    • 简介

    示意图

    • 跨进程通信的核心原理

    关于其核心原理:内存映射,具体请看文章:操作系统:图文详解 内存映射

    示意图

    3.3 模型原理步骤说明

    示意图

    3.4 额外说明

    说明1:Client进程、Server进程 & Service Manager 进程之间的交互 都必须通过Binder驱动(使用 openioctl文件操作函数),而非直接交互

    原因:

    1. Client进程、Server进程 & Service Manager进程属于进程空间的用户空间,不可进行进程间交互
    2. Binder驱动 属于 进程空间的 内核空间,可进行进程间 & 进程内交互

    所以,原理图可表示为以下:

    虚线表示并非直接交互

    示意图

    说明2: Binder驱动 & Service Manager进程 属于 Android基础架构(即系统已经实现好了);而Client 进程 和 Server 进程 属于Android应用层(需要开发者自己实现)

    所以,在进行跨进程通信时,开发者只需自定义Client & Server 进程 并 显式使用上述3个步骤,最终借助 Android的基本架构功能就可完成进程间通信

    示意图

    说明3:Binder请求的线程管理
    • Server进程会创建很多线程来处理Binder请求
    • Binder模型的线程管理 采用Binder驱动的线程池,并由Binder驱动自身进行管理

    而不是由Server进程来管理的

    • 一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。

    所以,在进程间通信时处理并发问题时,如使用ContentProvider时,它的CRUD(创建、检索、更新和删除)方法只能同时有16个线程同时工作


    • 至此,我相信大家对Binder 跨进程通信机制 模型 已经有了一个非常清晰的定性认识
    • 下面,我将通过一个实例,分析Binder跨进程通信机制 模型在 Android中的具体代码实现方式

    即分析 上述步骤在Android中具体是用代码如何实现的


    4. Binder机制 在Android中的具体实现原理

    • Binder机制在 Android中的实现主要依靠 Binder类,其实现了IBinder 接口

    下面会详细说明

    • 实例说明:Client进程 需要调用 Server进程的加法函数(将整数a和b相加)

    即:

    1. Client进程 需要传两个整数给 Server进程
    2. Server进程 需要把相加后的结果 返回给Client进程
    • 具体步骤
      下面,我会根据Binder 跨进程通信机制 模型的步骤进行分析

    步骤1:注册服务

    • 过程描述
      Server进程 通过Binder驱动 向 Service Manager进程 注册服务
    • 代码实现
      Server进程 创建 一个 Binder 对象
    1. Binder 实体是 Server进程 在 Binder 驱动中的存在形式
    2. 该对象保存 ServerServiceManager 的信息(保存在内核空间中)
    3. Binder 驱动通过 内核空间的Binder 实体 找到用户空间的Server对象
    • 代码分析
        
        Binder binder = new Stub();
        // 步骤1:创建Binder对象 ->>分析1
    
        // 步骤2:创建 IInterface 接口类 的匿名类
        // 创建前,需要预先定义 继承了IInterface 接口的接口 -->分析3
        IInterface plus = new IPlus(){
    
              // 确定Client进程需要调用的方法
              public int add(int a,int b) {
                   return a+b;
             }
    
              // 实现IInterface接口中唯一的方法
              public IBinder asBinder(){ 
                    return null ;
               }
    };
              // 步骤3
              binder.attachInterface(plus,"add two int");
             // 1. 将(add two int,plus)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
             // 2. 之后,Binder对象 可根据add two int通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
            // 分析完毕,跳出
    
    
    <-- 分析1:Stub类 -->
        public class Stub extends Binder {
        // 继承自Binder类 ->>分析2
    
              // 复写onTransact()
              @Override
              boolean onTransact(int code, Parcel data, Parcel reply, int flags){
              // 具体逻辑等到步骤3再具体讲解,此处先跳过
              switch (code) { 
                    case Stub.add: { 
    
                           data.enforceInterface("add two int"); 
    
                           int  arg0  = data.readInt();
                           int  arg1  = data.readInt();
    
                           int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
    
                            reply.writeInt(result); 
    
                            return true; 
                      }
               } 
          return super.onTransact(code, data, reply, flags); 
    
    }
    // 回到上面的步骤1,继续看步骤2
    
    <-- 分析2:Binder 类 -->
     public class Binder implement IBinder{
        // Binder机制在Android中的实现主要依靠的是Binder类,其实现了IBinder接口
        // IBinder接口:定义了远程操作对象的基本接口,代表了一种跨进程传输的能力
        // 系统会为每个实现了IBinder接口的对象提供跨进程传输能力
        // 即Binder类对象具备了跨进程传输的能力
    
            void attachInterface(IInterface plus, String descriptor);
            // 作用:
              // 1. 将(descriptor,plus)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
              // 2. 之后,Binder对象 可根据descriptor通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
    
            IInterface queryLocalInterface(Stringdescriptor) ;
            // 作用:根据 参数 descriptor 查找相应的IInterface对象(即plus引用)
    
            boolean onTransact(int code, Parcel data, Parcel reply, int flags);
            // 定义:继承自IBinder接口的
            // 作用:执行Client进程所请求的目标方法(子类需要复写)
            // 参数说明:
            // code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
            // data:目标方法的参数。(Client进程传进来的,此处就是整数a和b)
            // reply:目标方法执行后的结果(返回给Client进程)
             // 注:运行在Server进程的Binder线程池中;当Client进程发起远程请求时,远程请求会要求系统底层执行回调该方法
    
            final class BinderProxy implements IBinder {
             // 即Server进程创建的Binder对象的代理对象类
             // 该类属于Binder的内部类
            }
            // 回到分析1原处
    }
    
    <-- 分析3:IInterface接口实现类 -->
    
     public interface IPlus extends IInterface {
              // 继承自IInterface接口->>分析4
              // 定义需要实现的接口方法,即Client进程需要调用的方法
             public int add(int a,int b);
    // 返回步骤2
    }
    
    <-- 分析4:IInterface接口类 -->
    // 进程间通信定义的通用接口
    // 通过定义接口,然后再服务端实现接口、客户端调用接口,就可实现跨进程通信。
    public interface IInterface
    {
        // 只有一个方法:返回当前接口关联的 Binder 对象。
        public IBinder asBinder();
    }
      // 回到分析3原处
    

    注册服务后,Binder驱动持有 Server进程创建的Binder实体

    步骤2:获取服务

    • Client进程 使用 某个 service前(此处是 相加函数),须 通过Binder驱动 向 ServiceManager进程 获取相应的Service信息
    • 具体代码实现过程如下:

    示意图

    此时,Client进程与 Server进程已经建立了连接

    步骤3:使用服务

    Client进程 根据获取到的 Service信息(Binder代理对象),通过Binder驱动 建立与 该Service所在Server进程通信的链路,并开始使用服务

    • 过程描述

      1. Client进程 将参数(整数a和b)发送到Server进程
      2. Server进程 根据Client进程要求调用 目标方法(即加法函数)
      3. Server进程 将目标方法的结果(即加法后的结果)返回给Client进程
    • 代码实现过程

    步骤1: Client进程 将参数(整数a和b)发送到Server进程

    // 1. Client进程 将需要传送的数据写入到Parcel对象中
    // data = 数据 = 目标方法的参数(Client进程传进来的,此处就是整数a和b) + IInterface接口对象的标识符descriptor
      android.os.Parcel data = android.os.Parcel.obtain();
      data.writeInt(a); 
      data.writeInt(b); 
    
      data.writeInterfaceToken("add two int");;
      // 方法对象标识符让Server进程在Binder对象中根据"add two int"通过queryLocalIInterface()查找相应的IInterface对象(即Server创建的plus),Client进程需要调用的相加方法就在该对象中
    
      android.os.Parcel reply = android.os.Parcel.obtain();
      // reply:目标方法执行后的结果(此处是相加后的结果)
    
    // 2. 通过 调用代理对象的transact() 将 上述数据发送到Binder驱动
      binderproxy.transact(Stub.add, data, reply, 0)
      // 参数说明:
        // 1. Stub.add:目标方法的标识符(Client进程 和 Server进程 自身约定,可为任意)
        // 2. data :上述的Parcel对象
        // 3. reply:返回结果
        // 0:可不管
    
    // 注:在发送数据后,Client进程的该线程会暂时被挂起
    // 所以,若Server进程执行的耗时操作,请不要使用主线程,以防止ANR
    
    
    // 3. Binder驱动根据 代理对象 找到对应的真身Binder对象所在的Server 进程(系统自动执行)
    // 4. Binder驱动把 数据 发送到Server 进程中,并通知Server 进程执行解包(系统自动执行)
    
    

    步骤2:Server进程根据Client进要求 调用 目标方法(即加法函数)

    // 1. 收到Binder驱动通知后,Server 进程通过回调Binder对象onTransact()进行数据解包 & 调用目标方法
      public class Stub extends Binder {
    
              // 复写onTransact()
              @Override
              boolean onTransact(int code, Parcel data, Parcel reply, int flags){
              // code即在transact()中约定的目标方法的标识符
    
              switch (code) { 
                    case Stub.add: { 
                      // a. 解包Parcel中的数据
                           data.enforceInterface("add two int"); 
                            // a1. 解析目标方法对象的标识符
    
                           int  arg0  = data.readInt();
                           int  arg1  = data.readInt();
                           // a2. 获得目标方法的参数
                          
                           // b. 根据"add two int"通过queryLocalIInterface()获取相应的IInterface对象(即Server创建的plus)的引用,通过该对象引用调用方法
                           int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
                          
                            // c. 将计算结果写入到reply
                            reply.writeInt(result); 
                            
                            return true; 
                      }
               } 
          return super.onTransact(code, data, reply, flags); 
          // 2. 将结算结果返回 到Binder驱动
    
    
    

    步骤3:Server进程 将目标方法的结果(即加法后的结果)返回给Client进程

      // 1. Binder驱动根据 代理对象 沿原路 将结果返回 并通知Client进程获取返回结果
      // 2. 通过代理对象 接收结果(之前被挂起的线程被唤醒)
    
        binderproxy.transact(Stub.ADD, data, reply, 0);
        reply.readException();;
        result = reply.readInt();
              }
    }
    
    • 总结
      下面,我用一个原理图 & 流程图来总结步骤3的内容

    原理图

    流程图


    5. 优点

    对比 LinuxAndroid基于Linux)上的其他进程通信方式(管道、消息队列、共享内存、
    信号量、Socket),Binder 机制的优点有:
    示意图


    6. 总结

    • 本文主要详细讲解 跨进程通信模型 Binder机制 ,总结如下:

    定义

    特别地,对于从模型结构组成的Binder驱动来说:

    示意图

    • 整个Binder模型的原理步骤 & 源码分析

    示意图

    Carson带你学Android 文章系列:
    Carson带你学Android:页面活动-Activity
    Carson带你学Android:广播-BroadcastReceiver
    Carson带你学Android:服务-Service
    Carson带你学Android:内存承载器-ContentProvider


    欢迎关注Carson_Ho的CSDN博客 与 公众号!

    博客链接:https://carsonho.blog.csdn.net/


    请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!

    展开全文
  • 1. 先根据进程名查看进程id ps aux | grep 进程名(或者ps -ef | grep 进程名) y@ubuntu:~$ ps aux | grep bitcoind y 2708 101 12.1 1611172 488580 ? Ssl Aug22 32:04 bitcoind -daemon -connect=172.16.247.139 ...
    1. 先根据进程名查看进程id

    ps aux | grep 进程名(或者ps -ef | grep 进程名)

    y@ubuntu:~$ ps aux | grep bitcoind
    y          2708  101 12.1 1611172 488580 ?      Ssl  Aug22  32:04 bitcoind -daemon -connect=172.16.247.139
    y          3564  0.0  0.0  21536  1092 pts/0    S+   00:23   0:00 grep --color=auto bitcoind
    

    可以看到,bitcoind进程的pid是2708

    2.通过进程id查看占用的端口

    netstat -nap | grep 进程id

    y@ubuntu:~$ netstat -nap | grep 2708
    (Not all processes could be identified, non-owned process info
     will not be shown, you would have to be root to see it all.)
    tcp        0      0 127.0.0.1:8332          0.0.0.0:*               LISTEN      2708/bitcoind       
    tcp6       0      0 ::1:8332                :::*                    LISTEN      2708/bitcoind 
    

    可以看到,pid=2708的进程占用的端口号是8332

    3.通过端口号查看占用的进程id

    netstat -nap | grep 端口号

    y@ubuntu:~$ netstat -nap | grep 8332
    (Not all processes could be identified, non-owned process info
     will not be shown, you would have to be root to see it all.)
    tcp        0      0 127.0.0.1:8332          0.0.0.0:*               LISTEN      2708/bitcoind       
    tcp6       0      0 ::1:8332                :::*                    LISTEN      2708/bitcoind  
    

    可以看到,占用8332端口的是进程的pid是2708,名称是bitcoind

    展开全文
  • 线程VS进程

    万次阅读 2021-03-10 16:44:19
    什么是线程、什么是进程 在Java中要同时执行(如果是单核,准确的说是交替执行)多个任务,使用的是多线程,而要理解线程,我们先要了解什么是进程什么是线程。 一般的定义:进程是指在操作系统中正在运行的一个应用...
  • 查看Linux端口占用,并kill掉相关进程

    万次阅读 多人点赞 2018-06-13 16:57:49
    话不多说,本文介绍Linux常规操作:查看端口占用进程,根据PID kill掉相关进程。另外补充:根据程序名查看进程PID。 首先,两条命令,lsof命令和netstat命令。 方式一:lsof命令 1、查看占用端口进程的PID: ...
  • Linux中查看进程状态信息

    万次阅读 多人点赞 2018-10-24 22:00:21
    Linux中查看进程状态信息 一、常用命令总结 ps -l 列出与本次登录有关的进程信息; ps -aux 查询内存中进程信息; ps -aux | grep *** 查询***进程的详细信息; top 查看内存...
  • 有一次面试的时候,被问到进程之间有哪些通信方式,不过由于之前没深入思考且整理过,说的并不好。想必大家也都知道进程有哪些通信方式,可是我猜很多人都是靠着”背“来记忆的,所以今天的这篇文章,讲给大家详细着...
  • 进程间通讯的7种方式

    万次阅读 多人点赞 2019-04-26 14:23:24
    进程的亲缘关系通常是指父子进程关系。 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。...
  • Linux查看进程,关闭进程

    万次阅读 2019-06-09 19:54:32
    ps -aux //完整的显示当前用户的所有进程 ps -ef|grep <进程名> //查看并筛选 跟进程名有关的进程,该进程名可以是进程的全部或者部分。 kil -9 pid //关闭进程 ...
  • 有关于进程,线程and协程

    千次阅读 多人点赞 2021-07-02 00:49:08
    1.关于进程 ①百度百科:(点我跳转!) 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程...
  • linux使用Supervisor守护进程

    万次阅读 2021-07-15 11:24:56
    用Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制, 应用场景 执行耗时爬虫任务 守护队列,以及...
  • 进程和线程的深入理解

    万次阅读 多人点赞 2019-04-25 00:14:40
    进程和线程的深入理解精确概括进程和线程的区别 既然要谈区别,那么首先需要理解什么是进程和线程。 之前,我读到一篇材料,发现有一个很好的类比,可以把它们解释地清晰易懂。 1.计算机的核心是CPU,它承担了所有的...
  • Linux进程控制(精讲)

    万次阅读 多人点赞 2021-09-23 21:55:48
    文章目录进程创建fork函数初始fork函数返回值写时拷贝fork常规用法fork调用失败的原因进程终止进程退出场景进程常见退出方法_exit函数exit函数return退出进程等待进程等待的必要性进程等待的方法wait方法waitpid方法...
  • 进程管理

    千次阅读 多人点赞 2021-05-05 22:15:07
    进程管理
  • 进程和线程的主要区别(总结)

    万次阅读 多人点赞 2018-06-13 10:11:52
    根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类...
  • Linux系统编程——特殊进程之守护进程

    万次阅读 多人点赞 2015-05-25 19:02:32
    守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与...
  • android双进程守护,进程很难被杀死demo

    千次下载 热门讨论 2014-12-15 19:18:11
    android双进程守护,进程很难被杀死
  • 进程-进程标识符

    千次阅读 2017-05-17 19:15:02
    1.什么是进程标识符 系统给每个进程定义了一个唯一标识该进程的非负正数,称作进程标识符。当某一进程终止后,其标识符可以重新用作另一进程的标识符。不过,在任何时刻,一个标识符所代表的进程是唯一的。系统把...
  • windows 关闭java进程

    万次阅读 2021-02-02 15:21:09
    关闭全部java进程 查看进程列表 :tasklist 结束进程:taskkill /f /t /im java.exe
  • mysql中kill掉所有锁表的进程

    万次阅读 2013-09-30 16:02:59
    很多时候由于异常或程序错误会导致个别进程占用大量系统资源,需要结束这些进程,通常可以使用以下命令Kill进程: mysql中kill掉所有锁表的进程 2009-05-12 14:03 转载请保留如下作者信息 作者 : jesse ...
  • 假定父进程malloc的指针指向0x12345678, fork 后,子进程中的指针也是指向0x12345678,但是这两个地址都是虚拟内存地址 (virtual memory),经过内存地址转换后所对应的 物理地址是不一样的。所以两个进城中的这两个...
  • 进程进程相互独立 (可以通过socket套接字实现进程间通信,可以通过硬盘(文件)实现进程通信,也可以通过队列(Queue)实现进程通信) 子进程会拷贝复制主进程中的所有资源(变量、函数定义等),所以子进程比子线程耗费...
  • 进程 第二天 (fork函数&子进程与父进程&守护进程)

    万次阅读 多人点赞 2018-08-13 20:37:20
    详细标注:进程 第二天 (fork函数&amp;子进程与父进程&amp;守护进程) 一、fork()函数 在Linux系统内,创建子进程的方法是使用系统调用fork()函数。fork()函数是Linux系统内一个非常重要的函数,它与我们...
  • 前台进程  前台进程是Android系统中最重要的进程,是与用户正在交互的进程 可见进程 可见进程指部分程序界面能够被用户看见,却不在前台与用户交互。 服务进程  一个包含已启动服务的进程就是服务进程,...
  • 进程、线程和协程之间的区别和联系

    万次阅读 多人点赞 2018-10-06 14:56:24
    一、进程   进程,直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源,所以...
  • 使用进程名获取进程id和进程句柄

    万次阅读 2018-05-04 22:18:06
    本文内容为编写一个任务管理器所需要的API介绍以及如何通过进程名称获取进程id的方法,函数使用频率高,所以贴出来以便复用。 相关api介绍 CreateToolhelp32Snapshot 函数功能为拍摄当前所有...
  • Windows进程

    万次阅读 2020-06-23 21:46:48
    进程的创建 BOOL CreateProcess( LPCTSTR lpApplicationName, // name of executable module LPTSTR lpCommandLine, // command line string 命令行参数 LPSECURITY_ATTRIBUTES ...
  • 守护进程

    千次阅读 2019-09-02 14:49:40
    什么是守护进程? Daemon(精灵)进程是Linux中后台服务进程,独立于控制终端并且周期性地执行某种任务或等待处理某些发生事件,一般采用以d结尾的名字。 守护进程就是通常讲Daemon进程,是linux后台执行的一种...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,705,631
精华内容 1,082,252
关键字:

进程