并发:指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
并行:多个处理机同时进行处理。
上图为3个进程轮流被cpu处理,每次处理一个时间碎片的时间。
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。
/usr/src/linux-headers-3.16.0-30/include/linux/sched.h文件中可以查看struct task_struct 结构体定义。其内部成员有很多,我们重点掌握以下部分即可:
* 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
* 进程的状态,有就绪、运行、挂起、停止等状态。
* 进程切换时需要保存和恢复的一些CPU寄存器。
* 描述虚拟地址空间的信息。
* 描述控制终端的信息。
* 当前工作目录(Current Working Directory)。
* umask掩码。
* 文件描述符表,包含很多指向file结构体的指针。
* 和信号相关的信息。
* 用户id和组id。
* 会话(Session)和进程组。(父进程、子进程和兄弟进程在同一个进程组中,多个进程组组成一个会话)
* 进程可以使用的资源上限(Resource Limit)。
例:
例:
执行结果:
多次反复执行,有时会得到如下结果:
这是因为:
如果是下面这种方式创建多个子进程,那么会创建出多个不需要的进程:
所以要判断fork函数的返回值,当返回值为0时,说明当前是子进程,就不要再创建进程了,所以break。
当 i 等于0~4时,当前进程为子进程,当 i 等于5时,当前进程为父进程。
(1)使用execl函数,让子进程执行ls命令。
执行结果:
可以看到:++++++++++ i 这里的for循环只输出了一次,这是父进程输出的,子进程并没有输出。这是由于execl函数执行后,子进程的代码段就被替换为ls的代码了,所以执行ls输出后,子进程就结束了。
(2)使用execl函数,让子进程执行自己写的程序
执行结果:
(3)使用execlp函数,让子进程执行PATH环境变量能够搜索到的程序
(4)exec函数是有返回值的,但是没有用。因为如果execlp函数执行成功,那么子进程的代码段会被替换,所以后面的perror函数执行不到。而如果execlp函数执行失败,那么子进程的代码段没有被替换,那么后面的perror函数会输出错误信息,然后exit函数退出当前进程。代码如下:
(1)孤儿进程例子:
执行结果:
可以看到子进程被1492号进程(ubuntu下的init进程)领养了。
(2)僵尸进程例子:
该例子中父进程一直进行while循环,没有回收子进程,所以此时子进程成为僵尸进程。ps aux一下也可以看到:
40763号进程为父进程,还在进行while循环,40764号为僵尸进程。
如果想让僵尸进程消失,用kill直接杀它是不行的,因为它已经死了。我们可以将该僵尸进程的父进程用kill命令杀死,那么僵尸进程就会被init进程领养,然后init进程会释放僵尸进程的pcb,然后就会看到父进程和僵尸进程都不存在了。
例1:父进程等待子进程正常退出后,回收子进程。
执行结果:
例2:父进程等待子进程正常退出后,回收子进程,并打印子进程的退出状态。
执行结果:
例3:父进程等待子进程被杀死后,回收子进程,并打印杀死子进程的信号的编号。
执行结果: