Linux进程

文章详细介绍了Linux中的进程概念,包括程序与进程的区别、进程标识符PID、父进程与子进程的关系,以及进程的创建(fork)和退出方式。此外,还讨论了进程的存储空间分配,写时拷贝机制,并解释了为何需要等待子进程退出以及孤儿进程的概念。
摘要由CSDN通过智能技术生成

进程

  • 什么是程序、什么是进程,有什么区别?

    • 程序是静态的概念,编译生成的可执行文件叫程序。进程是程序运行起来,系统就多了一个进程。
  • 如何查看系统中有哪些进程?

    • ps:显示当前进程的状态
      • ps -aux:显示所有包含其他使用者的进程
      • ps -aux| grep init(init是过滤关键字) :查找指定进程
    • top:实时显示 process 的动态(也可显示进程信息,类似于windos平台下的任务管理器,可以查看进程资源的占用等等)
  • 什么是进程标识符?

    • 每一个进程都有一个非负整数表示唯一的ID,叫做PID,类似身份证
    • PID=0:称为交换进程(swapper)(作用:进程调度)
    • PID=1:init进程(作用:系统初始化)
    • C程序里可以getpid()函数可以获取该C程序进程ID号(具体可以使用命令man 2 getpid查看)
  • 什么叫父进程,什么叫子进程?

    • A进程创建了B进程,那么B进程称A进程为父进程,A进程称B进程为子进程
  • C程序的存储空间是如何分配的?

    • 程序运行时系统会分配一段内存,该内存的低内存地址从正文(代码段)开始,高地址(命令行参数和环境变量)
    • 正文(代码段)
      • 程序当中由基本语句形成的流程或是算法,如if else switch 这样的都是属于代码段
    • 数据段
      • 已初始化的数据段(.data):已初始化的变量属于数据段
      • 未初始化的数据(.bss) :函数外未初始化的变量属于bss段
      • 常量(.rodata):常量(宏定义,[const修饰过的变量还是变量,只是值不能被修改])
      • 函数调用,函数里的局部变量,函数产生的信息都存放在堆内存里边
      • 命令行参数和环境变量(mian函数的形参:argc,argv)
      • malloc申请动态内存空间
  • 父子进程间的存储空间的关系

    • 早期Linux系统设计子进程会直接拷贝父进程的如下部分内容

      • 正文、初始化的数据、未初始化的数据、堆、栈、命令行参数和环境变量、打开的文件、IO流等等
    • 后期Linux系统可以进行写时拷贝(选择性的拷贝)

      • 什么是写时拷贝?
        • Linux 写时复制机制原理 0 在 Linux 系统中,调用 fork 系统调用创建子进程时,并不会把父进程所有占用的内存页复制一份,而是与父进程共用相同的内存页,而当子进程或者父进程对内存页进行修改时才会进行复制 —— 这就是著名的写时复制机制。
    •   #include <stdio.h>
        #include <sys/types.h>
        #include <unistd.h>
        int main()
        {
           pid_t pid;
           int a = 10;
                    
           pid = fork();
                        
           if(pid > 0 ){
               sleep(1);
           }
           else if (pid == 0){
               a = a+100;
           }
           printf("a=%d\n",a);//父进程延时1S子进程先执行,父进程打印10,子进程打印110说明子进程拷贝了父进程的a=10,但是子进程修改a的值只限于子进程有效,父进程仍然是10
            return 0;
        }
      

创建进程

  • 如何创建一个进程?

    • pid_t fork(void);

    • 返回值说明
      0代表当前进程是子进程
      非负数代表当前进程为父进程
      -1调用失败
    • fork函数调用成功返回两次

    • #include <stdio.h>
      #include <sys/types.h>
      #include <unistd.h>
      
      int main()
      {
          pid_t pid;
       	pid_t pid2;
          pid_t retpid;
          
          pid = getpid();
          printf("before fork: PID = %d\n",pid);
          
          retpid = fork();//返回的是子进程PID
          
          pid2 = getpid();
          printf("after fork: PID = %d\n",pid2);
          //严格意义上说并不是返回两次,而是返回值大于0意味着是父进程成功创建子进程fork返回子进程PID号,子进程会拷贝父进程的相关信息,拷贝给子进程的retpid被赋值为0(像是初始化为0一样)
          if(pid == pid2){
              printf("This is father print,pid = %d\n",retpid);
          }
          else {
              printf("This is child print, pid = %d retpid=%dn",getpid(),retpid);
          }
          return 0;
      }
      
    • 补充:

      • vfork函数也可以创建进程,与fork有如下区别
        • vfork直接使用父进程存储空间,不拷贝
        • vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行

创建子进程的目标

  • 一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中时常见的—父进程等待客户端的服务请求。当这种请求达到时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达
  • 一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec

进程退出

  • 正常退出
  1. Main函数调用return
  2. 进程调用exit(),标准c库
  3. 进程调用_exit() 或则 _Exit(),属于系统调用
  4. 进程最后一个线程返回
  5. 最后一个线程调用pthread_exit
  • 异常退出
  1. 调用abor
  2. 当进程受到某些信号时,如Ctrl+c
  3. 最后一个线程对取消请求做出响应
  • 退出函数

    1. void exit(int staus);

    2. void _exit(int staus);

    3. void _Exit(int staus);

    • 1函数是对2和3的封装

等待子进程

  • 为什么要等待子进程退出?

    • 创建子进程是希望子进程去”干活“,需要关心子进程”干活“干得怎么样,干完有干完的状态(exit(?)退出),没干完也有没干完的状态(abort,还是被杀死了?),父进程不仅要等待还要收集子进程退出的状态
    • 子进程退出状态不被收集,变成僵死进程(僵尸进程)
  • 等待函数

    • pid_t wait(int *status);

    • pid_t waitpid(pid_t pid,id_t *status,int options);

    • int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);(用得比较少)

    • wait和waitpid的区别(但是status是一样的):

      • wait使调用者阻塞,waitpid有一个选项,可以使调用者不阻塞(使用waitpid收集子进程状态,子进程好像也会变成会僵尸进程)

    • status说明
      非空子进程退出状态放在它所指向的地址中
      不关心退出状态
  • 解析退出状态

    • #include <stdio.h>
      #include <sys/types.h>
      #include <unistd.h>
      #include <stdlib.h>
      #include <sys/wait.h>
      int main ()
      {
          pid_t num;
          int a=0;
          int status=0;
          num =fork();
          if(num == -1){
              printf("making course file\n");
          }
          else if(num == 0) {
              while(1){
                  printf("This is son course    %d \n",getpid());
                  sleep(1);
                  a++;
                  if(a==3) {
                      exit(3);
                  }
              }
          }
          else if(num > 0) {
              wait(&status);//收集子进程退出状态
              printf("son course exit status =%d\n",WEXITSTATUS(status));//使用宏WEXITSTATUS对退出状态进行解析才能得到exit退出状态3
              while(1) {
                  sleep(1);
                  printf("This is father course %d \n",getpid());
                  printf("a=%d\n",a);
              }
          }
          return 0;
      }
      
  • 什么是孤儿进程?

    • 父进程如果不等待子进程退出,在子进程之前就结束了自己的”生命“,此时子进程叫做孤儿进程
    • Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Trt_ToHoldOn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值