1-1关于Linux的进程详述

1-1关于Linux的进程详述

之所以希望对Linux有一个尽可能全面地了解,是因为我认为进程是Linux中非常重要的一环。

本章将了解以下内容:
1.什么是进程?
2.进程和程序有何区别?
3.进程知识的一些拓展
4.关于线程控制

1.什么是进程

进程是进程是程序在一个数据集合上的一次执行过程。
广义的进程其实有许多概念:
进程是一个独立的可调度的活动;
进程是一个抽象实体,当它执行某个任务时,要分配和释放各种资源;
进程是可以并行执行的计算单位;
进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动;

2. 进程和程序有何区别?

2.1 概念

  程序(procedure):程序就是执行一系列有逻辑、有顺序结构的指令,帮我们达成某个结果。我(或者说系统)对于它的期望就是:这个操作有什么效果。比如考试,老师给你一张试卷->你开始做->老师看到你的成绩,考试就是程序,你需要执行,写了就有分数,哪怕零分。
  进程(process):进程是程序在一个数据集合上的一次执行过程,在早期的UNIX、Linux 2.4及更早的版本中,它是系统进行资源分配和调度的独立基本单位。就如上面的考试(程序),老师让你考试你执行了一个考试的程序,你做了“听力”这个进程,又做了“单项选择题”这个进程,还做了“作文“这个进程。你需要一步一步来,一题一题做。
  为了方便理解,可以简而言之:程序是为了实现某种任务而设计的软件。进程?进行中的程序。

2.2 进程的特性

  程序只是一些列指令的集合,是一个静止的实体,而进程不同,进程有以下的特性:
(1)动态性:进程的实质是一次程序执行的过程,有创建、撤销等状态的变化。而程序是一个静态的实体。
(2)并发性:进程可以做到在一个时间段内,有多个程序在运行中。程序只是静态的实体,所以不存在并发性。
(3)独立性:进程可以独立分配资源,独立接受调度,独立地运行。
(4)异步性:进程以不可预知的速度向前推进。
(5)结构性:进程拥有代码段、数据段、PCB(进程控制块,进程存在的唯一标志)。也正是因为有结构性,进程才可以做到独立地运行。

2.3 进程的分类

  在Linux系统中,根据进程的特点,把进程可以分为三类:交互进程、批处理进程和守护进程。
  交互进程:是由shell启动的进程,它既可以在前台运行,也可以在后台运行。交互进程在执行过程中,要求与用户进行交互操作。简单来说就是用户需要给出某些参数或者信息,进程才能继续执行
  批处理进程:与windows原来的批处理很类似,是一个进程序列。该进程负责按照顺序启动其它进程。
  守护进程:是是执行特定功能或者执行系统相关任务的后台进程。守护进程只是一个特殊的进程,不是内核的组成部分。许多守护进程在系统启动时启动,直到系统关闭时才停止运行。而某些守护进程只是在需要时才会启动,比如FTP或者Apache服务等,可以在需要的时候才启动该服务。
  根据进程的状态又可以将其分为:守护进程、孤儿进程和僵尸进程(《在UNIX高级环境编程》书中也称为”僵死进程“)。
  守护进程:所有守护进程都可以超级用户(用户ID为0)的优先权运行;守护进程没有控制终端;守护进程的父进程都是init进程(即1号进程)。
  孤儿进程:一个父进程退出后,它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程(没有爸爸,要找个后爸ヽミ ´∀`ミノ<管着它)。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。
  僵尸进程:一个子进程结束但是没有完全释放内存(在内核中的 task_struct没有释放),该进程就成为僵尸进程。僵尸进程会导致资源的浪费,而孤儿进程不会。关于孤儿进程和僵死进程会开一版贴来进一步认识。

3.进程拓展

  进程有父子之分,那父子进程如何实现关系?
  进程产生子进程由fork(vfork)创建。fork是一个系统调用,其效果是为当前进程创建一个新进程,此子进程(新进程)除了父进程的返回值和PID外都是一样的,如进程的文件描述、寄存器等等。vfork可以使的效率得到大幅度提升。
  另一个系统调用exec() ,作用是切换子进程中的执行程序也就是替换其从父进程复制过来的代码段与数据段。
  以下为简短的代码演示使用fork创建子进程:

int      glob = 6;      /* external variable in initialized data */
char     buf[] = "a write to stdout\n";

int main(int argc, char const *argv[])
{
	int      var;       /* automatic variable on the stack */
	pid_t    pid;

	var = 88;
	if (write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) - 1)
		err_sys("write error");
	printf("before fork\n");   /* we don't flush stdout */
	if ((pid = fork()) < 0)    /*(1)*/
	{
		err_sys("fork error");
	}
	else if (pid == 0)         /* child */   /*(2)*/
	{
		glob++;                /* modify variables */
		var++;
	}
	else                       /*(3)*/
	{
		sleep(2);              /* parent */
	}
	printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
	exit(0);
}   //摘自《UNIX高级环境编程第二版》

  毋须多关心代码除了(1)、(2)、(3)的其他内容。在(1)处创建子进程。在fork函数调用之后,新的进程将启动并和本进程一起从fork函数返回。但不同的是本进程的fork将返回新任务的pid,而新进程的fork将返回0。(注意就算创建成功,也还没有走相关的程序,需要exec())。
  关于运行中的进程,我们可以通过进程树来方便的查看其父子关系。系统提供了进程树查看工具“tree”。去下查看树指令“pstree”:

systemd─┬─ModemManager─┬─{gdbus}
        │              └─{gmain}
        ├─NetworkManager─┬─dhclient
        │                ├─dnsmasq
        │                ├─{gdbus}
        │                └─{gmain}
        ├─VGAuthService
        ├─accounts-daemon─┬─{gdbus}
        │                 └─{gmain}
        ├─acpid
        ├─agetty
        ├─apt.systemd.dai───apt.systemd.dai───unattended-upgr
        ├─aptd───{gmain}
        ├─avahi-daemon───avahi-daemon
        ├─colord─┬─{gdbus}
        │        └─{gmain}
        ├─cron
        ├─cups-browsed─┬─{gdbus}
        │              └─{gmain}
        ├─cupsd
        ├─dbus-daemon
        ├─fwupd─┬─3*[{GUsbEventThread}]
        │       ├─{fwupd}
        │       ├─{gdbus}
        │       └─{gmain}
        ├─gnome-keyring-d─┬─{gdbus}
        │                 ├─{gmain}
        │                 └─{timer}
        ├─lightdm─┬─Xorg───{InputThread}
        │         ├─lightdm─┬─upstart─┬─at-spi-bus-laun─┬─dbus-daemon
        │         │         │         │                 ├─{dconf worke+
        │         │         │         │                 ├─{gdbus}
        │         │         │         │                 └─{gmain}
        │         │         │         ├─at-spi2-registr─┬─{gdbus}
        │         │         │         │                 └─{gmain}

  可以直观地看到所有进程起始于“systemd”(另外一种是init),其即是所有进程的父进程或者祖父进程(简单记忆就是爸爸的爸爸)。
  我们也可以使用<ps -fxo user,ppid,pid,pgid,command>来进一步确认进程的父子关系。pid即该进程的唯一标号,ppid即其父进程编号,command 表示的是该进程通过执行什么样的命令或者脚本而产生的。通过查看此表就可以知道ps是由bash 创建的。

USER       PPID    PID   PGID COMMAND
apical-+  21070  21071  21071 bash
apical-+  21071  23394  23394  \_ ps -fxo user,ppid,pid,pgid,command
apical-+  16212  16213  16213 bash
apical-+  15848  15849  15849 bash
apical-+  15766  15767  15767 bash
apical-+  14318  14319  14319 bash
apical-+  13738  13739  13739 bash
apical-+  13333  13334  13334 bash
apical-+   1708   2646   2646 /sbin/upstart --user
apical-+   2646   2711   2709  \_ upstart-udev-bridge --daemon --user
apical-+   2646   2724   2724  \_ dbus-daemon --fork --session --addres
apical-+   2646   2736   2736  \_ /usr/lib/x86_64-linux-gnu/hud/window-
apical-+   2646   2767   2765  \_ upstart-dbus-bridge --daemon --sessio
apical-+   2646   2768   2766  \_ upstart-dbus-bridge --daemon --system
apical-+   2646   2770   2769  \_ upstart-file-bridge --daemon --user
apical-+   2646   2774   2773  \_ /usr/bin/fcitx
apical-+   2646   2788   2788  \_ /usr/bin/dbus-daemon --fork --print-p
apical-+   2646   2794   2793  \_ /usr/bin/fcitx-dbus-watcher unix:abst
apical-+   2646   2801   2801  \_ /usr/lib/x86_64-linux-gnu/bamf/bamfda

  完整地了解UNIX的进程控制是非常重要的。其中必须熟练掌握的只有几个函数–fork 、exec_族、_exit、wait和waitpid 。很多应用程序都使用这些原语。fork原语也给了我们一个了解竞争条件的机会。往后的文章逐步对这些函数进行学习。

参考:https://www.jianshu.com/p/b96f0c3d2d36
   https://blog.csdn.net/qq_36812792/article/details/80118923

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值