Unix_Linux操作系统-笔记Day7(进程)

Day7

基本概念

  1. 进程与程序

    • 程序:存储在磁盘上的文件,包含可执行指令和数据的静态实体
    • 进程:运行中的程序(一个程序可以执行多次,加载出多个进程),进入活动状态的计算机程序。
  2. 进程的分类:

    • 交互进程:有输入,输出,用户可以根据自己的情况输入数据,得到想要的结果(一般进程)。
    • 批处理进程:由脚本加载执行的程序(Linux下的shell,win下的bat)
    • 守护进程:总是活跃的,后台运行,一般由系统开机时加载执行或root用户加载执行。
  3. 查看进程

    • 简单方式:ps,显示出当前用户有终端控制权的进程信息
    • 列表形式:ps -aux,以列表形式显示详细信息
      • a 所有用户的终端控制进程
      • x 所有用户的无终端控制的进程
      • u 详细方式显示
  4. 进程的详细信息列表

    • USER 进程的用户
    • PID 进程的id
    • %CPU 进程的cpu使用率
    • %MEM 内存的使用率
    • VSZ 占用虚拟内存的大小
    • RSS 占用物理内存的大小
    • TTY 终端的次设备号,如果无终端控制显示?
    • STAT 进程的状态
      • O 就绪态,等待被系统调度
      • R 运行态,Linux无就绪态,归于R
      • S 休眠态,可以被系统中断(信号)唤醒转入运行态
      • T 暂停态,是被SIGSTOP信号暂停,由SIGCONT信号转入运行态
      • Z 僵尸态,已经结束停止运行,但父进程还没有回收
      • < 高优先级进程
      • N 低优先级进程
      • l 多线程化的进程
      • + 在前台进程组中的进程
      • s 会话首进程
    • START TIME 进程的开始时间
    • COMMAND 程序可执行文件名
  5. 父进程与子进程,孤儿进程与僵尸进程

    一个进程A可以创建出另一个进程B,创建者叫fu进程,被创建进程叫子进程,父进程启动子进程后,在操作系统的调用下父进程同时执行(同步)。

    如果子进程先于父进程结束,会向父进程发送SIGCHLD信号,父进程收到信号后,就应该去回收子进程的相关资源,但在默认情况下,父进程忽略该信号

    当子进程结束后,父进程没有回收子进程的资源,子进程变成了僵尸进程

    如果父进程先于子进程结束,子进程就变成了孤儿进程,同时被孤儿院收养(init),然后就变成了init 的子进程

  6. 进程标识符

    操作系统会为每个进程分配一个唯一的标识符,采用无符号整数表示,即进程ID

    进程ID在任何时候都是唯一的,但是可以重用,当一进程结束,新创建的进程才可以使用它的进程ID(延时重用)

    • getpid 获取进程ID
    • getppid 获取父进程ID
    • getuid 获取当前进程的用户ID
    • getgid 获取当前进程组ID

pid_t fork(void);

  • 创建一个新进程
  • 返回值 一次调用两次返回,失败返回-1(当进程数超出系统限制,创建失败)
  • 两次返回分别是子进程ID和0,父进程会拿到子进程的ID,子进程返回0,借此可以分别出父子进程0
  • 通过fork创建的子进程就是父进程的副本(拷贝)
  • 子进程会获取父进程的数据段,bss段,堆,栈,IO流(共享文件指针和文件描述符),缓冲区拷贝,与父进程共享代码段。
  • 子进程会继承父进程的信号处理方式
  • fork函数调用后父子进程各自执行,谁先返回不一定,但可以使用一些手法来确保谁先执行
  • 僵尸进程与孤儿进程的实现
#include <sidio.h>
#include <unistd.h>

int main(){

    pid_t id = fork();
    if(id == 0){
        //子进程分支
        printf("我的子进程%u,我的父进程是%u\n",getpid(),getppid());
    }else{
        //父进程分支
        printf("我是父进程%u,我的子进程是%u\n",getpid(),id);
    }
}


int main(){
    printf("*");
    fork();
    printf("*");
    fork();
}

练习1 实现一个程序来验证子进程确实拷贝了父进程的数据段,bss段,堆,栈,IO流

homework

练习2 为一个父进程创建5个子进程,一共六个进程

homework

进程的正常退出

  1. 从main函数中return

  2. 调用标准库中的exit函数
    void exit(int status);

    • 调用者立即结束该进程
    • status 退出代码,可以在父进程中获取到,子进程留给父进程的遗言
    • 推出前做的事
      1. 先调用事先注册的函数(通过atexit/on_exit)

        • int on_exit(void (*function)(int , void *), void *arg);
          • function 函数指针,无返回值,参数1为exit函数的参数,参数2,为on_exit函数的第二个参数
          • arg 传给function的第二个参数
        • int atexit(void (*function)(void));
          • 注册一个函数,当进程通过exit函数开始结束时调用
          • function 函数指针,无返回值无参数
        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        
        void onfunc(int num, void* arr){
            printf("%s %d %s\n",__func__,num,(char*)arr);
        }
        
        void atfunc(void){
            printf("我要死了。。\n");
        }
        
        int main(){
            on_exit(onfunc,"silesile...");
            atexit(atfunc);
            exit(10);
        }
        
      2. 冲刷所有处在未关闭状态的标准IO流,删除所有临时文件

      3. 返回一个整数(EXIT_SUCCESS/EXIT_FAILURE)给操作系统

      4. 该函数不会返回,它的功能借助了_exit/_Exit函数

  3. 调用_exit/_Exit函数退出

    • 这两个函数的功能是一样的
    #include <unistd.h>
    void _exit(int status);
    #include <stdlib.h>
    void _Exit(int status);//调用系统的_exit
    
    • 调用的进程会结束,没有返回值
    • status 会被父进程获取到(低八位,一个字节)
      1. 进程结束前会关闭所有处于打开状态的文件描述符

      2. 把所有子进程托付给孤儿院(init)

      3. 向它的父进程发送SIGCHLD信号

        注意 exit函数也执行以上操作,因此它底层调用了_exit/_Exit

  4. 进程的最后一个线程执行最后一条语句

  5. 进程的最后一个线程调用了pthread_exit函数

进程的异常退出

  1. 调用了abort函数,该函数会产生SIGABRT信号
  2. 进程接收到一些信号(无设捕获处理,或无法捕获处理)
  3. 进程的最后一个线程接收到"取消"请求,并作出响应,相当于线程接收到了结束信号

wait/waitpid

  • pid_t wait(int *wstatus);

    • 等待所有的子进程结束,并获取到最终的状态码,只要有一个进程结束就立即返回
    1. 应该是父进程收到子进程发来的SIGCHLD信号时,调用wait函数回收子进程的资源比赛获取结束状态
    2. 如果所有子进程都在运行,则wait阻塞
    3. 如果已有僵尸进程,wait也会立即返回,回收资源获取结束状态码
    4. 如果没有子进程,则返回失败-1
  • pid_t waitpid(pid_t pid, int *wstatus, int options);

    • 等待指定的进程结束,并获取到最终的状态码
    • pid
      • -1 等待任一子进程结束,此时与wait等价
      • >0 等待进程号为pid的进程结束,此时只等待一个进程结束
      • =0 等待同组的任意一子进程结束,此时等待的是整个进程组
      • <-1 等待的是进程组id是pid的绝对值中的任一子进程结束,此时等待的是整个进程组
    • options
      • WNOHANG 非阻塞模式,如果没有子进程结束则立即退出
      • WUNTRACED 如果子进程处理暂停,则返回它的状态
      • WCONTINUED 如果子进程从暂停转为继续,则返回它的状态
    1. wait函数只能孤独地等待子进程结束,而waitpid可以有更多的选择
    2. waitpid不光可以等待子进程,也可以等待同组进程
    3. waitpid可以阻塞也可以不阻塞
    4. 也可以监控子进程的暂停或继续状态
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

void sigchld(int sig){
    int status = 0;
    pid_t id = wait(&status);//waitpid
    printf("我的孩子%u,去世了...5555!!!,它的遗言是%d\n",id,WEXITSTATUS(status));
}

int main(){
    signal(SIGCHLD,sigchld);
    if(fork()){
        printf("我是进程%u\n",getpid());
        pasue();
    }else{
        sleep(3);
        printf("我是进程%u\n",getpid());
        return 
    }
}

vfork

pid_t vfork(void);

  • 功能 与fork的功能基本一致
  • 区别 通过vfork创建的进程不复制父进程的的地址空间,必须通过excl系列函数加载自己的可执行程序
  • 当执行vforks时,子进程先返回,此时它占用了父进程的地址空间,当子进程成功创建后(通过excl加载可执行程序).父进程才返回

execl

  • 加载子进程的可执行文件

int execl(const char *path, const char *arg, ...);

  • path 可执行文件的路径
  • arg 第一个main函数的参数,最后一次必须以NULL结尾

int execlp(const char *file, const char *arg, ...);

  • file 可执行文件的名字,会从PATH环境变量表的路径中查找可执行文件并执行

int execle(const char *path, const char *arg, ..., char * const envp[]);

  • envp 父进程的环境变量表,传递给子进程

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[], char *const envp[]);



int main(){
    pid_t id = vfork();
    if(id){
        printf("我是子进程%u,我的父进程是%u\n",getpid(),getppid());
        execl("./a.out","a.out",NULL);
    }else{
        printf("我是父进程%u,我的子进程是%u",getpid(),id);
        pause();
    }
}

system

int system(const char *command);

  • 执行系统命令的,也可以加载可执行程序
  • 相当于创建了一个子进程,但子进程不结束,该函数不返回,父子进不会同时执行
  • command
#include <stdio.h>
#include <stdlib.h>

int main(){

    system("./a.out");
    printf("我是父进程\n");
    pause();
}

练习 实现system函数

homework

进程组

  • 是一个或多个进程的集合,每个进程除有一个进程ID还有一个进程祖ID,进程组中的进程归属同一个作业控制(负责完成同一个任务)
  • 每个进程都有一个组长,组长的进程ID就是组ID
  • 同一进程组的进程,会统一接收到终端的信号,由fork创建的子进程,默认加入父进程的进程组
    pid_t getpgid(pid_t pid);
  • 获取pid进程的进程组id

int setpgid(pid_t pid, pid_t pgid);

  • 设置进程pid的进程组id
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值