UNIXC002 进程创建,终止,遗言函数

进程的基础知识

1. 程序和进程

在这里插入图片描述

2. 查看进程信息

在这里插入图片描述

3. 进程的状态

在这里插入图片描述
在这里插入图片描述

进程的创建

1. fork 创建一个子进程

在这里插入图片描述

  • 通俗的讲,这个函数调用一次但可能"返回两次"。
  • 父进程中调用fork,在创建子进程成功的情况下,会在父进程中返回子进程pid,并会在子进程中返回0。
  • 如果没有创建成功,只会在父进程中返回-1。

2. 代码示例

t_stdio.h

#ifndef T_STDIO_H_
#define T_STDIO_H_
#include <stdio.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)
#endif

fork.c

#include "t_stdio.h"
#include <unistd.h>

int main(void){
    pid_t pid = fork();
    
    if(pid == -1)E_MSG("fork", -1);
    if(pid == 0){
        // 在子进程中执行的代码
        printf("child process ...\n");
    } else {
        // 在父进程中执行的代码
        printf("parent process ...\n");
    }
        // 父子进程都会执行的代码
    printf("parent and child process ...\n");
    return 0;
}
# 有可能先打印 parent process..., 也有可能先打印 child process ...
# 取决与系统的调度, 并且父进程是不会等子进程的,除非用到wait等系统调用
$ ./a.out 
parent process ...
parent and child process ...
child process ...
parent and child process ...

3. 父子进程共享父进程PCB

在这里插入图片描述

  • 子进程和父进程共享父进程的PCB,节省了资源开销

在这里插入图片描述

  • 上图是父子进程的代码段,一模一样,因为子进程和父进程用的映像(代码段,数据段, 栈段, 堆等)是同一个(只有写时才复制相应的部分)。只不过根据fork返回值的不同,父子进程执行了不同代码而已。
  • 左图是父进程的代码段,当父进程中fork函数执行完,如果返回值是-1时,那么子进程不会存在了(右图不会出现)。
  • 如果父进程fork函数成功了,返回值就是子进程的pid。右图出现,子进程和父进程共用一个映像。 父进程继续会执行 printf(“parent process …\n”); 然后继续执行 printf(“parent and child process …\n”);
  • 子进程中的fork函数返回值是0, 子进程会执行 printf(“child process …\n”);printf(“parent and child process …\n”);

进程的终止

1. return 和 exit(3) 的区别

在这里插入图片描述

  • 0377 是8进制,转成2进制就是11 111 111,exit 会将参数的值转为2进制,然后和 11 111 111做与运算(只要低八位),然后转为10进制返回给父进程
  • ./a.out 创建的进程的父进程就是当前这个 bash
  • echo $? 显示当前命令的返回值

2. 代码示例

exit.c

#include <stdlib.h>
#include <stdio.h>
int main(void){
    // getchar();
    exit(3);
    return 0;
}
// $ gcc exit.c 
// $ ./a.out 
// $ echo $?
// 3

#include <stdlib.h>
#include <stdio.h>
int main(void){
    // -1的二进制表示方式(低8位)是11 111 11
    exit(-1); //return -1; 结果好像也是255
    return 0;
}
// $ gcc exit.c 
// $ ./a.out 
// $ echo $?
// 255

遗言函数

1. 什么是遗言函数

在这里插入图片描述

2. 向进程注册遗言函数 atexit(3)

在这里插入图片描述

  • atexit(3)的参数变量是一个函数指针类型的
  • 子进程继承父进程的遗言函数,因为父子进程共享同一个映像,所以父进程能执行的遗言函数,子进程终止时也能执行。

atexit.c

#include <stdlib.h>
#include "t_stdio.h"
#include <unistd.h>
// 进程的遗言函数
void bye(void){
    printf("bye...\n");
}
void goodbye(void){
    printf("goodbye...\n");
}
int main(void){
    // 向进程注册遗言函数
    atexit(bye);
    atexit(goodbye);

    // 验证子进程继承父进程的遗言函数
    // 创建子进程需要在注册之后,不然无法继承父进程的遗言函数
    pid_t pid=fork();
    if(pid == -1)E_MSG("fork", -1);
    return 0;
}
# 观察输出发现,遗言函数的注册顺序和调用顺序相反
$ ./a.out 
goodbye...
bye...
goodbye...
bye...

3. 向进程注册遗言函数 on_exit(3)

在这里插入图片描述

#include <stdlib.h>
#include "t_stdio.h"
#include <unistd.h>
// 遗言函数的定义
// return 或 exit后面的参数(没有与0377做与运算)会传递给n
void bye(int n, void *arg){
    printf("bye...%d...%s\n", n, (char *)arg);
}

int main(void){
    // 向进程注册遗言函数, 一样,调用顺序和注册顺序相反
    on_exit(bye, "hello");
    // 验证子进程继承父进程的遗言函数
    // 创建子进程需要在注册之后,不然无法继承父进程的遗言函数
    pid_t pid=fork();
    if(pid == -1)E_MSG("fork", -1);
    exit(-1);
    // return -1;
}
$ ./a.out 
bye...-1...hello
bye...-1...hello
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值