1. 什么是进程
…
在提出这个问题的时候,我想了一下,大概就是内核执行的一个程序(错误回答)吧。但是这么说,连我自己下次看都不明白在说什么。于是我查了一下,它代表着CPU所能处理的单个任务,及运行实例。
在面向进程设计的系统(如早期 UNIX,Linux 2.4及更早版本中),进程是程序的基本执行实体;
在面向线程设计的系统(当代多数操作系统、Linux 2.6及更新版本中),进程本身不是基本运行单位,而是线程的容器
先看一个简单的例子。
一个 C 程序的启动终止过程
下图演示的是一个最为普遍的一个C程序的启动过程,main 函数调用用户函数,返回值后退出。
- 内核使程序执行的唯一方法是调用一个
exec
函数 - 启动例程从内核获取 命令行参数 和 环境变量值,为调用
main
函数做好准备 - 调用返回
- 调用
exit
函数或者执行return
语句 exit
函数调用终止处理程序
后,再调用_exit
或者_Exit
立即进入内核
C 程序的存储空间布局
- 正文:该部分为 CPU 执行的机器指令部分。只读
- 初始化数据(数据段):需初始化的变量,即任何函数之外的声明
- 未初始化数据(bss段):
- 堆:自动变量和函数调用时保存的信息
- 栈:动态存储分配
2. 进程控制
进程控制原语:创建新进程、执行程序、进程终止
进程标识:每个进程都有一个非负整数表示的唯一进程 ID,可复用(延迟复用算法)
创建新进程
一个现有的进程可以调用函数 fork
创建新进程。
例如
#include <stdio.h>
#include <unistd.h>
int globvar = 6;
int
main(void)
{
int var;
pid_t pid;
var = 88;
if (pid = fork() < 0) {
perror("fork failed!\n");
} else if (pid == 0) {
/* 子进程 */
globvar++;
var++;
} else {
/* 父进程 */
sleep(2);
}
printf("pid = %ld, glob = %d, var = %d\n",
(long)getpid(), globvar, var);
exit(0);
}
这个程序创建一个进程,在子进程里对变量加 1,父进程睡眠 2s,主动进入阻塞。启动终止流程如下
- main 函数这次调用的是 fork 函数
- fork 函数复制了数据段、堆、栈,但是父子进程共享正文段
但是事实上很多实现并不执行一个父进程数据段、堆、栈的完全副本。使用写时复制(Copy-On-Write,COW)技术,即父子进程共享数据段、堆、栈(只读),如果父子进程中的任一个试图修改这些区域,内核只为修改区域的那块内存制作一个副本。
父进程阻塞
fork 之后,父子进程各自执行程序,wait 可以使父进程阻塞等待子进程先执行。
#include <sys/wait.h>
pid_t wait(int *statloc);
/*
* pid == -1 等待任一子进程
* pid > 0 等待进程 id 于 pid 相等的子进程
* pid == 0 等待组 id 等于调用进程组 id 的任一子进程
* pid < -1 等待组 id 等于 pid 绝对值的任一子进程
*/
pid_t waitpid(pid_t pid, int *statloc, int options);
当调用 wait 之后
- 如果所有子进程都在运行,则父进程阻塞
- 如果一个子进程终止,则取得该子进程的终止状态立即返回
- 如果没有任何子进程,则立即出错返回
执行程序
当进程调用 exec 程序族,该进程执行的程序完全替换为新的程序(继承?)。
终止程序
exit
**函数 system **
system 在其实现中调用了 fork、exec、waitpid,用于总结进程原语的使用。
system 函数用于在程序中执行一个命令。
- system 创建一个子进程
- 在子进程中执行输入的命令行程序
- 在父进程中等待程序执行完毕
8-13-system.c 文件:
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
int
system(const char *cmdstring)
{
pid_t pid;
int status;
if (cmdstring == NULL) {
return (1);
}
if ((pid = fork()) < 0) {
status = -1;
} else if (pid == 0) {
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127);
} else {
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR) {
status = -1;
break;
}
}
}
return (status);
}
8-13-system-test.c 测试文件:
#include "apue.h"
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int status;
if ((status = system("date")) < 0) {
err_sys("system() error");
}
pr_exit(status);
if ((status = system("nosuchcommand")) < 0) {
err_sys("system() error");
}
pr_exit(status);
if ((status = system("who; exit 44")) < 0) {
err_sys("system() error");
}
pr_exit(status);
return 0;
}
编译:
> gcc 8-13-system.c 8-13-system-test.c -g -O2 -o system.out -lapue
执行:
> ~/test$ ./system.out
2020年 04月 15日 星期三 00:31:57 CST
normal termination, exit status = 0
sh: 1: nosuchcommand: not found
normal termination, exit status = 127
renz :0 2020-04-15 05:52 (:0)
normal termination, exit status = 44