进程
进程控制块PCB
PCB 进程控制块:
进程 id
文件描述符表
进程状态: 初始态、就绪态、运行态、挂起态、终止态。
进程工作目录位置
*umask 掩码 (进程的概念)
信号相关信息资源。
用户 id 和组 id
ps aux 返回结果里,第二列是进程 id
进程状态
环境变量
echo $PATH 查看环境变量
path 环境变量里记录了一系列的值,当运行一个可执行文件时,系统会去环境变量记录的位置里查找这个文件并执行。
echo $TERM 查看终端
echo $LANG 查看语言
env 查看所有环境变量
fork()函数
创建子进程
fork 函数:
pid_t fork(void)
创建子进程。父子进程各自返回。父进程返回子进程 pid。 子进程返回 0.
getpid();getppid();
循环创建 N 个子进程模型。 每个子进程标识自己的身份。
父子进程相同:
刚 fork 后。 data 段、text 段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位
置、信号处理方式
父子进程不同:
进程 id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集
父子进程共享:
读时共享、写时复制。———————— 全局变量。
1. 文件描述符 2. mmap 映射区
getpid() 获取当前进程 getppid() 获取当前进程的父进程
所有父进程的子进程 pid=父进程 pid+1,而孤儿进程的父进程均为 1630,
循环创建多个子进程
for
fork
刚创建的子进程会与父进程争夺CPU ,随机的
孤儿进程和僵尸进程
孤儿进程:
父进程先于子进终止,子进程沦为“孤儿进程”,会被 init 进程领养。
僵尸进程:
子进程终止,父进程尚未对子进程进行回收,在此期间,子进程为“僵尸进程”。 kill 对其无效。这里要注意,每个进程结束后都必然会经历僵尸态,时间长短的差别而已。
子进程终止时,子进程残留资源 PCB 存放于内核中,PCB 记录了进程结束原因,进程回收就是回
收 PCB。回收僵尸进程,得 kill 它的父进程,让孤儿院init去回收它。
exec函数
exec 函数族一般规律:
exec 函数一旦调用成功,即执行新的程序,不返回。只有失败才返回,错误值-1,所以通常我们
直接在 exec 函数调用后直接调用 perror(),和 exit(),无需 if 判断。
l(list)
命令行参数列表
p(path)
搜索 file 时使用 path 变量
v(vector)
使用命令行参数数组
e(environment)
使用环境变量数组,不适用进程原有的环境变量,设置新加载程序运行的
环境变量
execl 来让子程序调用自定义的程序。
int execl(const char path, const char arg, …)
这里要注意,和 execlp 不同的是,第一个参数是路径,不是文件名。
这个路径用相对路径和绝对路径都行
int execlp(const char file, const char arg, …)
成功,无返回,失败返回-1
参数 1:要加载的程序名字,该函数需要配合 PATH 环境变量来使用,当 PATH 所有目录搜素后没
有参数 1 则返回出错。
该函数通常用来调用系统程序。如 ls、date、cp、cat 命令。
execlp 这里面的 p,表示要借助环境变量来加载可执行文件
execve是真正的系统调用,其他五个
进入了exec程序就不用继续执行后面的代码块
wait 回收进程
wait 函数:
回收子进程退出资源, 阻塞回收任意一个。
pid_t wait(int *status)
参数:(传出) 回收进程的状态。
返回值:成功: 回收进程的 pid
失败: -1, errno
函数作用 1: 阻塞等待子进程退出
函数作用 2: 清理子进程残留在内核的 pcb 资源
函数作用 3: 通过传出参数,得到子进程结束状态
一个进程终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的 PCB 还保留着,内
核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用 wait 或者 waitpid 获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在 shell 中用特殊变量$?查看,因为 shell 是它的父进程,当它终止时,shell 调用 wait 或者 waitpid 得到它的退出状态,同时彻底清除掉这个进程。
获取子进程退出值和异常终止信号
获取子进程正常终止值:
WIFEXITED(status) —》 为真 —》调用 WEXITSTATUS(status) —》 得到子进程退出值。
获取导致子进程异常终止信号:
WIFSIGNALED(status) —》 为真 —》调用 WTERMSIG(status) —》 得到导致子进程异常终止的信号编号。
if (WIFEXITED(status)) { //为真,说明子进程正常终止.printf("child exit with %d\n", WEXITSTATUS(status));}if (WIFSIGNALED(status)) { //为真,说明子进程是被信号终止.printf("child kill with signal %d\n", WTERMSIG(status));}
waitpid()回收子进程
waitpid 函数: 指定某一个进程进行回收。可以设置非阻塞。
waitpid(-1, &status, 0) == wait(&status);
pid_t waitpid(pid_t pid, int *status, int options)
参数:
pid:指定回收某一个子进程 pid
0: 待回收的子进程 pid
-1:任意子进程
0:同组的子进程。
status:(传出) 回收进程的状态。
options:WNOHANG 指定回收方式为,非阻塞。
返回值:
>0 : 表成功回收的子进程 pid
0 : 函数调用时, 参 3 指定了 WNOHANG, 并且,没有子进程结束。
-1: 失败。errno
一次 wait/waitpid 函数调用,只能回收一个子进程。上一个例子,父进程产生了 5 个子进程,
wait 会随机回收一个,捡到哪个算哪个
回收多个子进程
while ((wpid = waitpid(-1, NULL, WNOHANG)) != -1) { //使用非阻塞方式,回收子进程.if (wpid > 0) {printf("wait child %d \n", wpid);} else if (wpid == 0) {sleep(1);continue;}}
子进程运行完了父进程就回收
wait、waitpid 一次调用,回收一个子进程。 只能回收子进程,一次调用回收一个
想回收多个。while
会话SID
会话:多个进程组的集合
创建会话的 6 点注意事项:
1. 调用进程不能是进程组组长,该进程变成新会话首进程
2. 该进程成为一个新进程组的组长进程
3. 需要 root 权限(ubuntu 不需要)
4. 新会话丢弃原有的控制终端,该会话没有控制终端
5. 该调用进程是组长进程,则出错返回
6. 建立新会话时,先调用 fork,父进程终止,子进程调用 setsid
getsid 函数:
pid_t getsid(pid_t pid) 获取当前进程的会话 id
成功返回调用进程会话 ID,失败返回-1,设置 error
setsid 函数:
pid_t setsid(void) 创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID
成功返回调用进程的会话 ID,失败返回-1,设置 error
调用setsid函数的进程 既是新的会长 也是新的组长。
守护进程
Daemon 精灵进程 是Linux的后台服务进程 通常独立于控制中断并且周期性地执行某种任务或者等待处理某些发生的时间 。 一般采用以d结尾的名字。
Linux 后台的一些系统服务进程,没有控制中断,不能直接和用户交互,不受用户登录 注销的影响,一直在运行着,他们都是守护进程,如 :预读入缓输入机制的实现:ftp服务器 nfs服务器
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leder
创建守护进程
- 创建子进程 父进程退出
所以工作在子进程中进行形式上脱离了控制中断
- 在子进程中创建会话
a. setsid()函数
b. 使子进程完全独立出来 脱离控制
- 改变当前目录为根目录
- chdir()函数
- 防止占用可卸载的文件系统
- 也可以换成其他路径
- 重设文件权限掩码
- umask()函数
- 设置权限 八进制的参数
- 防止继承文件创建屏蔽字拒绝某些权限
- 增加守护进程灵活性
- 关闭文件描述符
- 继承的打开文件不会用到 浪费系统资源 无法卸载
- 开始执行守护进程核心工作守护进程退出处理程序模型
…..
简言之
1,fork子进程,让父进程终止
2,子进程调用setsid()创建新会话
3,通常根据需要,改变工作目录位置chdir()
4,根据需要,重设umask文件权限掩码//影响进程对文件的权限
5,根据需要,关闭/重定向 文件描述符 012 /dev/null
fd = open(“/dev/null”,O_RDWR);
dup2(fd,STDOUT_FILNO); //重定向 stdout 和 stderr
dup2(fd,STDERR_FILNO);
标准输出(stdout)指的就是在命令行里,每次你输入指令后,终端上打印出来的那些话,那些反馈。标准错误(stderr)跟标准输出差不多,只不过是程序出错时反馈的内容。标准输入(stdin)就是程序指示让你输入用户名密码之类的这种,这里不多谈输入。
6,守护进程,业务逻辑。while()
