文档章节

Linux基础 -- Linux中的进程

兴趣使然的程序员
 兴趣使然的程序员
发布于 2017/09/07 23:02
字数 4961
阅读 16
收藏 0
点赞 0
评论 0

Linux 中也难免遇到某个程序无响应的情况,可以通过一些命令来帮助我们让系统能够更流畅的运行。

而在此之前,我们需要对进程的基础知识有一定的了解,才能更好、更有效率的使用Linux 提供的工具。

1、进程、线程的基础概念

首先程序与进程是什么?程序与进程又有什么区别?

程序(procedure):不太精确地说,程序就是执行一系列有逻辑、有顺序结构的指令,帮我们达成某个结果。就如我们去餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,最后我们得到了这么一盘牛肉盖浇饭。它需要去执行,不然它就像一本武功秘籍,放在那里等人翻看。

进程(process):进程是程序在一个数据集合上的一次执行过程,在早期的UNIX、Linux 2.4及更早的版本中,它是系统进行资源分配和调度的独立基本单位。同上一个例子,就如我们去了餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,而里面做饭的是一个进程,做牛肉汤汁的是一个进程,把牛肉汤汁与饭混合在一起的是一个进程,把饭端上桌的是一个进程。它就像是我们在看武功秘籍这么一个过程,然后一个篇章一个篇章地去练。

简单来说,程序是为了完成某种任务而设计的软件,比如 vim 是程序。什么是进程呢?进程就是运行中的程序

程序只是一些列指令的集合,是一个静止的实体,而进程不同,进程有以下的特性:

  • 动态性:进程的实质是一次程序执行的过程,有创建、撤销等状态的变化。而程序是一个静态的实体。
  • 并发性:进程可以做到在一个时间段内,有多个程序在运行中。程序只是静态的实体,所以不存在并发性。
  • 独立性:进程可以独立分配资源,独立接受调度,独立地运行。
  • 异步性:进程以不可预知的速度向前推进。
  • 结构性:进程拥有代码段、数据段、PCB(进程控制块,进程存在的唯一标志)。也正是因为有结构性,进程才可以做到独立地运行。
  • 并发:在一个时间段内,宏观来看有多个程序都在活动,有条不紊的执行(每一瞬间只有一个在执行,只是在一段时间有多个程序都执行过)

    并行:在每一个瞬间,都有多个程序都在同时执行,这个必须有多个 CPU 才行

    引入进程是因为传统意义上的程序已经不足以描述 OS 中各种活动之间的动态性、并发性、独立性还有相互制约性。程序就像一个公司,只是一些证书,文件的堆积(静态实体)。而当公司运作起来就有各个部门的区分,财务部,技术部,销售部等等,就像各个进程,各个部门之间可以独立运做,也可以有交互(独立性、并发性)

    而随着程序的发展越做越大,又会继续细分,从而引入了线程的概念,当代多数操作系统、Linux 2.6及更新的版本中,进程本身不是基本运行单位,而是线程的容器。就像上述所说的,每个部门又会细分为各个工作小组(线程),而工作小组需要的资源需要向上级(进程)申请。

    线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。因为线程中几乎不包含系统资源,所以执行更快、更有效率。

    简而言之,一个程序至少有一个进程,一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

2、进程的属性

2.1、进程的分类

大概明白进程是个什么样的存在后,我们需要进一步了解的就是进程分类。可以从两个角度来分:

  • 以进程的功能与服务的对象来分;
  • 以应用程序的服务类型来分;

第一个角度来看,我们可以分为用户进程与系统进程:

  • 用户进程:通过执行用户程序、应用程序或称之为内核之外的系统程序而产生的进程,此类进程可以在用户的控制下运行或关闭。
  • 系统进程:通过执行系统内核程序而产生的进程,比如可以执行内存资源分配和进程切换等相对底层的工作;而且该进程的运行不受用户的干预,即使是 root 用户也不能干预系统进程的运行。

第二角度来看,我们可以将进程分为交互进程、批处理进程、守护进程

  • 交互进程由一个 shell 终端启动的进程,在执行过程中,需要与用户进行交互操作,可以运行于前台,也可以运行在后台。
  • 批处理进程:该进程是一个进程集合,负责按顺序启动其他的进程
  • 守护进程:守护进程是一直运行的一种进程,在 Linux 系统启动时启动,在系统关闭时终止。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。例如 httpd 进程,一直处于运行状态,等待用户的访问。还有经常用的 cron(在 centOS 系列为 crond)进程,这个进程为 crontab 的守护进程,可以周期性的执行用户设定的某些任务。

2.2 进程的衍生

进程有这么多的种类,那么进程之间定是有相关性的,而这些有关联性的进程又是如何产生的,如何衍生的?

就比如我们启动了终端,就是启动了一个 bash 进程,我们可以在 bash 中再输入 bash 则会再启动一个 bash 的进程,此时第二个 bash 进程就是由第一个 bash 进程创建出来的,他们直接又是个什么关系?

我们一般称呼第一个 bash 进程是第二 bash 进程的父进程,第二 bash 进程是第一个 bash 进程的子进程,这层关系是如何得来的呢?

关于父进程与子进程便会提及这两个系统调用 fork() 与 exec()

fork-exec是由 Dennis M. Ritchie 创造的

fork() 是一个系统调用(system call),它的主要作用就是为当前的进程创建一个新的进程,这个新的进程就是它的子进程,这个子进程除了父进程的返回值和 PID 以外其他的都一模一样,如进程的执行代码段,内存信息,文件描述,寄存器状态等等

exec() 也是系统调用,作用是切换子进程中的执行程序也就是替换其从父进程复制过来的代码段与数据段

子进程就是父进程通过系统调用 fork() 而产生的复制品fork() 就是把父进程的 PCB 等进程的数据结构信息直接复制过来,只是修改了 PID,所以一模一样,只有在执行 exec() 之后才会不同,而早先的 fork() 比较消耗资源后来进化成 vfork(),效率高了不少,感兴趣的同学可以查查为什么。

这就是子进程产生的由来。简单的实现逻辑就如下方所示【注释1】

pid_t p;

p = fork();
if (p == (pid_t) -1)
        /* ERROR */
else if (p == 0)
        /* CHILD */
else
        /* PARENT */

既然子进程是通过父进程而衍生出来的,那么子进程的退出与资源的回收定然与父进程有很大的相关性。当一个子进程要正常的终止运行时,或者该进程结束时它的主函数 main() 会执行 exit(n); 或者 return n,这里的返回值 n 是一个信号,系统会把这个 SIGCHLD 信号传给其父进程,当然若是异常终止也往往是因为这个信号。

在将要结束时的子进程代码执行部分已经结束执行了,系统的资源也基本归还给系统了,但若是其进程的进程控制块(PCB)仍驻留在内存中,而它的 PCB 还在,代表这个进程还存在(因为 PCB 就是进程存在的唯一标志,里面有 PID 等消息),并没有消亡,这样的进程称之为僵尸进程(Zombie)。

如图中第四列标题是 S,S 表示的是进程的状态,而在下属的第三行的 Z 表示的是 Zombie 的意思。( ps 命令将在后续详解)

实验楼

正常情况下,父进程会收到两个返回值:exit code(SIGCHLD 信号)与 reason for termination 。之后,父进程会使用 wait(&status) 系统调用以获取子进程的退出状态,然后内核就可以从内存中释放已结束的子进程的 PCB;而如若父进程没有这么做的话,子进程的 PCB 就会一直驻留在内存中,一直留在系统中成为僵尸进程(Zombie)。

虽然僵尸进程是已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,在进程列表中保留一个位置,记载该进程的退出状态等信息供其父进程收集,从而释放它。但是 Linux 系统中能使用的 PID 是有限的,如果系统中存在有大量的僵尸进程,系统将会因为没有可用的 PID 从而导致不能产生新的进程。

另外如果父进程结束(非正常的结束),未能及时收回子进程,子进程仍在运行,这样的子进程称之为孤儿进程。在 Linux 系统中,孤儿进程一般会被 init 进程所“收养”,成为 init 的子进程。由 init 来做善后处理,所以它并不至于像僵尸进程那样无人问津,不管不顾,大量存在会有危害。

进程 0 是系统引导时创建的一个特殊进程,也称之为内核初始化,其最后一个动作就是调用 fork() 创建出一个子进程运行 /sbin/init 可执行文件,而该进程就是 PID=1 的进程 1,而进程 0 就转为交换进程(也被称为空闲进程),进程 1 (init 进程)是第一个用户态的进程,再由它不断调用 fork() 来创建系统里其他的进程,所以它是所有进程的父进程或者祖先进程。同时它是一个守护程序,直到计算机关机才会停止。

通过以下的命令我们可以很明显的看到这样的结构:

[root@izwz9gtdx1ch4f9gn56b32z ~]# pstree
systemd─┬─AliYunDun───14*[{AliYunDun}]
        ├─AliYunDunUpdate───3*[{AliYunDunUpdate}]
        ├─agetty
        ├─aliyun-service
        ├─atd
        ├─auditd───{auditd}
        ├─crond
        ├─dbus-daemon
        ├─lvmetad
        ├─ntpd
        ├─polkitd───5*[{polkitd}]
        ├─rsyslogd───2*[{rsyslogd}]
        ├─sshd───sshd───bash───pstree
        ├─systemd-journal
        ├─systemd-logind
        ├─systemd-udevd
        ├─tuned───4*[{tuned}]
        └─wrapper─┬─java───14*[{java}]
                  └─{wrapper}

由于我使用的CentOS 7系统中,init已经被systemd替代,如果是旧版本的系统,应该是下面这样的结构:

实验楼

通过以上的显示结果我们可以看的很清楚,init(systemd) 为所有进程的父进程或者说是祖先进程

我们还可以使用这样一个命令来看,其中 pid 就是该进程的一个唯一编号ppid 就是该进程的父进程的 pid,command 表示的是该进程通过执行什么样的命令或者脚本而产生的。

ps -fxo user,ppid,pid,pgid,command
[root@izwz9gtdx1ch4f9gn56b32z ~]# ps -fxo user,ppid,pid,pgid,command
USER      PPID   PID  PGID COMMAND
root         0     2     0 [kthreadd]
root         2     3     0  \_ [ksoftirqd/0]
root         2     5     0  \_ [kworker/0:0H]
root         2     7     0  \_ [migration/0]
root         2     8     0  \_ [rcu_bh]
root         2     9     0  \_ [rcu_sched]
root         2    10     0  \_ [watchdog/0]
root         2    12     0  \_ [khelper]
root         2    13     0  \_ [kdevtmpfs]
root         2    14     0  \_ [netns]
root         2    15     0  \_ [khungtaskd]
root         2    16     0  \_ [writeback]
root         2    17     0  \_ [kintegrityd]
root         2    18     0  \_ [bioset]
root         2    19     0  \_ [kblockd]
root         2    20     0  \_ [md]
root         2    26     0  \_ [kswapd0]
root         2    27     0  \_ [ksmd]
root         2    28     0  \_ [khugepaged]
root         2    29     0  \_ [fsnotify_mark]
root         2    30     0  \_ [crypto]
root         2    38     0  \_ [kthrotld]
root         2    40     0  \_ [kmpath_rdacd]
root         2    41     0  \_ [kpsmoused]
root         2    42     0  \_ [ipv6_addrconf]
root         2    61     0  \_ [deferwq]
root         2    92     0  \_ [kauditd]
root         2   211     0  \_ [ata_sff]
root         2   230     0  \_ [scsi_eh_0]
root         2   231     0  \_ [scsi_tmf_0]
root         2   232     0  \_ [scsi_eh_1]
root         2   233     0  \_ [scsi_tmf_1]
root         2   235     0  \_ [ttm_swap]
root         2   249     0  \_ [kworker/0:1H]
root         2   256     0  \_ [jbd2/vda1-8]
root         2   257     0  \_ [ext4-rsv-conver]
root         2   371     0  \_ [vballoon]
root         2 11685     0  \_ [kworker/u2:0]
root         2  9968     0  \_ [kworker/u2:1]
root         2 13223     0  \_ [kworker/0:2]
root         2 13269     0  \_ [kworker/0:0]
root         2 13319     0  \_ [kworker/0:1]
root         0     1     1 /usr/lib/systemd/systemd --switched-root --system --deserialize
root         1   325   325 /usr/lib/systemd/systemd-journald
root         1   336   336 /usr/lib/systemd/systemd-udevd
root         1   421   421 /sbin/auditd -n
root         1   451   451 /usr/sbin/rsyslogd -n
root         1   455   455 /usr/lib/systemd/systemd-logind
root         1   457   457 /usr/sbin/crond -n
root         1   460   460 /usr/sbin/atd -f
root         1   473   473 /sbin/agetty --noclear tty1 linux
root         1   687   687 /usr/bin/python -Es /usr/sbin/tuned -l -P
root         1  2040  2040 /usr/sbin/sshd
root      2040 13247 13247  \_ sshd: root@pts/0
root     13247 13249 13249      \_ -bash
root     13249 13361 13361          \_ ps -fxo user,ppid,pid,pgid,command
root         1  2133  2133 /usr/sbin/aliyun-service
root         1  8257  8257 /usr/local/aegis/aegis_update/AliYunDunUpdate
root         1  8314  8314 /usr/local/aegis/aegis_client/aegis_10_31/AliYunDun
root         1 26956 26955 /usr/local/cloudmonitor/wrapper/bin/./wrapper /usr/local/cloudm
root     26956 26958 26955  \_ /usr/local/cloudmonitor/jre/bin/java -Djava.compiler=none -
root         1 15925 15925 /usr/sbin/lvmetad -f

可以在图中看见我们执行的 ps 就是由 zsh 通过 fork-exec 创建的子进程而执行的

使用这样的一个命令我们也能清楚的看见 init(systemd )如上文所说是由进程 0 这个初始化进程来创建而出的子进程,而其他的进程基本是由 init(systemd ) 创建的子进程,或者是由它的子进程创建出来的子进程。所以 init (systemd )是用户进程的第一个进程也是所有用户进程的父进程或者祖先进程。

就像一个树状图,而 init (systemd )进程就是这棵树的根,其他进程由根不断的发散,开枝散叶。

2.3 进程组与 Sessions

每一个进程都会是一个进程组的成员,而且这个进程组是唯一存在的,他们是依靠 PGID(process group ID)来区别的,而每当一个进程被创建的时候,它便会成为其父进程所在组中的一员

一般情况,进程组的 PGID 等同于进程组的第一个成员的 PID,并且这样的进程称为该进程组的领导者,也就是领导进程,进程一般通过使用 getpgrp() 系统调用来寻找其所在组的 PGID,领导进程可以先终结,此时进程组依然存在,并持有相同的PGID,直到进程组中最后一个进程终结

与进程组类似,每当一个进程被创建的时候,它便会成为其父进程所在 Session 中的一员,每一个进程组都会在一个 Session 中,并且这个 Session 是唯一存在的,

Session 主要是针对一个 tty 建立,Session 中的每个进程都称为一个工作(job)。每个会话可以连接一个终端(control terminal)。当控制终端有输入输出时,都传递给该会话的前台进程组。

Session 意义在于将多个 jobs 囊括在一个终端,并取其中的一个 job 作为前台,来直接接收该终端的输入输出以及终端信号。 其他 jobs 在后台运行。

前台(foreground)就是在终端中运行,能与你有交互的

后台(background)就是在终端中运行,但是你并不能与其任何的交互,也不会显示其执行的过程

2.4 工作管理

bash(Bourne-Again shell)支持工作控制(job control),而 sh(Bourne shell)并不支持。

并且每个终端或者说 bash 只能管理当前终端的中的 job,不能管理其他终端中的 job。比如我当前存在两个 bash 分别为 bash1、bash2,bash1 只能管理其自己里面的 job 并不能管理 bash2 里面的 job。

  • 我们都知道当一个进程在前台运作时我们可以用 ctrl + c 来终止它,但是若是在后台的话就不行了。
  • 我们可以通过 & 这个符号,让我们的命令在后台中运行。

 例如:

[root@izwz9gtdx1ch4f9gn56b32z ~]# ll &
[2] 13493
[root@izwz9gtdx1ch4f9gn56b32z ~]# total 8
-rw-r--r-- 1 root root 4 Aug 29 13:19 msg2.c
-rw-r--r-- 1 root root 4 Aug 29 13:17 msg.c

[2]-  Done                    ls --color=auto -l --color=auto

图中所显示的 [2] 13493分别是该 job 的 job number 与该进程的 PID,而最后一行的 Done 表示该命令已经在后台执行完毕。

  • 我们还可以通过 ctrl + z 使我们的当前工作停止并丢到后台中去。(注意工作已经被暂停)。

例如:

实验楼

  • 被停止并放置在后台的工作我们可以使用jobs这个命令来查看
[root@izwz9gtdx1ch4f9gn56b32z ~]# jobs
[1]+  Stopped                 man

其中第一列显示的为被放置后台 job 的编号,而第二列的  表示最近(刚刚、最后)被放置后台的 job,同时也表示预设的工作,也就是若是有什么针对后台 job 的操作,首先对预设的 job,- 表示倒数第二(也就是在预设之前的一个)被放置后台的工作,倒数第三个(再之前的)以后都不会有这样的符号修饰第三列表示它们的状态,而最后一列表示该进程执行的命令

  • 我们可以通过这样的一个命令将后台的工作拿到前台来:
#后面不加参数提取预设工作,加参数提取指定工作的编号
#ubuntu 在 zsh 中需要 %,在 bash 中不需要 %
fg [%jobnumber]

实验楼

实验楼

  • 之前我们通过 ctrl + z 使得工作停止放置在后台,若是我们想让其在后台运作我们就使用这样一个命令
#与fg类似,加参则指定,不加参则取预设
bg [%jobnumber]

实验楼

  • 既然有方法将被放置在后台的工作提至前台或者让它从停止变成继续运行在后台,当然也有方法删除一个工作,或者重启等等
#kill的使用格式如下
kill -signal %jobnumber

#signal从1-64个信号值可以选择,可以这样查看
kill -l

 其中常用的有这些信号值

信号值 作用
-1 重新读取参数运行,类似与restart
-2 如同 ctrl+c 的操作退出 
-9 强制终止该任务   
-15 正常的方式终止该任务

实验楼

注意:

  • 若是在使用kill+信号值然后直接加 pid,你将会对 pid 对应的进程进行操作
  • 若是在使用kill+信号值然后 %jobnumber,这时所操作的对象是 job,这个数字就是就当前 bash 中后台的运行的 job 的 ID

 

本文转载自:https://www.shiyanlou.com/courses/1/labs/1943/document

共有 人打赏支持
兴趣使然的程序员
粉丝 21
博文 112
码字总数 87412
作品 0
深圳
程序员
嵌入式Linux学习基础规划篇

嵌入式的学习是需要日积月累的,是通过一点一滴的积累才能成为大神。下面来介绍一下嵌入式linux学习基础规划,目标是达到适应嵌入式应用软件开发、嵌入式系统开发或嵌入式驱动开发的基本素质...

创客学院
04/10
0
0
Android安全模型之Linux安全模型

Android系统以Linux内核为基础,理解Android的安全设计首先要理清Linux安全模型的主要概念与元素,包括用户与权限,进程与内存空间等。 用户与权限 Linux安全模型的基础是用户与用户组。Lin...

柳哥
2014/11/30
0
0
学着学着Linux系统写篇心得

本学期对于linux系统的学习,和自己买的一本《linux就该这么学》这本书,让我对linux有了一定的了解。我知道了linux只是个内核。现在的linux操作系统,都是用这么一个内核,加上其它的应用程...

linuxprobe16
06/03
0
0
Oracle数据库集群入门培训教程_Oracle RAC集群体系架构解析

课程目标 风哥本课程讲解Oracle数据库基础入门培训教程之Oracle RAC集群体系架构,内容包括:集群基本概念、Oracle RAC集群架构、Oracle RAC集群相关术语、Oracle RAC集群相关进程、Oracle ...

风哥Oracle
06/28
0
0
后端程序员必备的Linux基础知识

本文同步更新在我的Github上:网页链接 我自己总结的Java学习的系统知识点以及面试问题,目前已经开源,会一直完善下去,欢迎建议和指导欢迎Star: https://github.com/Snailclimb/Java-Gui...

Amsour丶
07/06
0
0
浅入浅出 Android 安全:第二章 Android Linux 内核层安全

第二章 Android Linux 内核层安全 来源:Yury Zhauniarovich | Publications 译者:飞龙 协议:CC BY-NC-SA 4.0 作为最广为人知的开源项目之一,Linux 已经被证明是一个安全,可信和稳定的软...

apachecn_飞龙
2016/11/30
0
0
前端&后端程序员必备的Linux基础知识

刚刚把文章改了名字:《后端程序员必备的Linux基础知识》->《前端&后端程序员必备的Linux基础知识》。 本文同步更新在我的Github上:网页链接 我自己总结的Java学习的系统知识点以及面试问题...

SuShine
07/09
0
0
Python标准库06 子进程 (subprocess包)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢! 谢谢Tolbkni Kao帮我纠正错误 这里的内容以Linux进程基础和Linux文本流为基础。subprocess包主要功能是...

张旭0512
2014/05/27
0
0
16个桌面Linux用户必须要知道的Shell命令

有些人仍然会有这中愚蠢的想法,他们认为使用Linux就必须使用Linux shell命令。胡说!你可以不懂得任何Linux命令,比如说ps,grep,ls等,但是你仍然可以使用很多现代的Linux桌面发行版。 Li...

王振威
2012/05/06
0
28
超实用的8个Linux命令行性能监测工具

摘要:本文总结了8个非常实用的Linux命令行性能监测工具,这些命令支持所有的Linux系统,不仅可以用于监控系统,还可以发现导致性能问题的原因所在。 对每个系统/网络管理员来说,每天监测L...

Neo_
2012/09/25
0
2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

【面试题】盲人坐飞机

有100位乘客乘坐飞机,其中有一位是盲人,每位乘客都按自己的座位号就坐。由于盲人看不见自己的座位号,所以他可能会坐错位置,而自己的座位被占的乘客会随便找个座位就坐。问所有乘客都坐对...

garkey
52分钟前
0
0
谈谈神秘的ES6——(二)ES6的变量

谈谈神秘的ES6——(二)ES6的变量 我们在《零基础入门JavaScript》的时候就说过,在ES5里,变量是有弊端的,我们先来回顾一下。 首先,在ES5中,我们所有的变量都是通过关键字var来定义的。...

JandenMa
今天
1
0
arts-week1

Algorithm 594. Longest Harmonious Subsequence - LeetCode 274. H-Index - LeetCode 219. Contains Duplicate II - LeetCode 217. Contains Duplicate - LeetCode 438. Find All Anagrams ......

yysue
今天
0
0
NNS拍卖合约

前言 关于NNS的介绍,这里就不多做描述,相关的信息可以查看NNS的白皮书http://doc.neons.name/zh_CN/latest/nns_background.html。 首先nns中使用的竞价货币是sgas,关于sgas介绍可以戳htt...

红烧飞鱼
今天
1
0
Java IO类库之管道流PipeInputStream与PipeOutputStream

一、java管道流介绍 在java多线程通信中管道通信是一种重要的通信方式,在java中我们通过配套使用管道输出流PipedOutputStream和管道输入流PipedInputStream完成线程间通信。多线程管道通信的...

老韭菜
今天
0
0
用Python绘制红楼梦词云图,竟然发现了这个!

Python在数据分析中越来越受欢迎,已经达到了统计学家对R的喜爱程度,Python的拥护者们当然不会落后于R,开发了一个个好玩的数据分析工具,下面我们来看看如何使用Python,来读红楼梦,绘制小...

猫咪编程
今天
1
0
Java中 发出请求获取别人的数据(阿里云 查询IP归属地)

1.效果 调用阿里云的接口 去定位IP地址 2. 代码 /** * 1. Java中远程调用方法 * http://localhost:8080/mavenssm20180519/invokingUrl.action * @Title: invokingUrl * @Description: * @ret......

Lucky_Me
今天
1
0
protobuf学习笔记

相关文档 Protocol buffers(protobuf)入门简介及性能分析 Protobuf学习 - 入门

OSC_fly
昨天
0
0
Mybaties入门介绍

Mybaties和Hibernate是我们在Java开发中应用的比较多的两个ORM框架。当然,目前Mybaties正在慢慢取代Hibernate,这是因为相比较Hibernate而言Mybaties性能更好,响应更快,更加灵活。我们在开...

王子城
昨天
2
0
编程学习笔记之python深入之装饰器案例及说明文档[图]

编程学习笔记之python深入之装饰器案例及说明文档[图] 装饰器即在不对一个函数体进行任何修改,以及不改变整体的原本意思的情况下,增加函数功能的新函数,因为这个新函数对旧函数进行了装饰...

原创小博客
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部