Linux学习(1)--------进程概念

进程与程序

  • 程序:完成特定任务的一系列指令集合。
  • 进程:程序的一次动态执行过程。(用户角度)
  • 进程是操作系统分配资源的基本单位,也是最小单位。每个进程有自己的独立地址空间和执行状态。 操作系统内核一定要有一个PCB来管理进程。(OS角度)

  • 程序:代码段+数据段

  • 进程:代码段+数据段+堆栈+PCB

  • 进程和程序的区别:
    进程是动态的。程序是静态的。
    进程的生命周期短暂,程序永久。

  • 一个进程只能对应一个程序,一个程序可以对应多个进程。

  • CPU访问外设:中断、轮询、DMA。(CPU与外设是由总线连接的)

  • 并发:在一个时间段内,宏观来看有多个程序都在活动,有条不许的执行
  • 并行:在每一个瞬间,都有多个程序都在同时执行,这个必须有多个CPU才醒
  • 线程:是操作系统能够运行运算调度的最小单位,他被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个但依顺序的控制流,一个进程中可以并发多个线程,每个线程并行执行不同的任务,因为线程中个几乎不包含资源,所有执行更快,更有效率
  • 简而言之,一个程序至少有一个进程,一个进程至少有一个线程,线程的划分尺度小于进程,使得多线程程序的并发性高

进程的状态

运行态:该进程正在运行,即进程正在占用CPU。
就绪态:进程已经具备执行的一切条件,正在等待分配CPU的处理时间片。
等待态:进程正在等待某个事件或某个资源。等待态又分为可中断等待和不可中断等待两种。可中断的等待进程可被信号中断,而不可中断的等待进程不能被信号中断。
停止状态:当进程收到一个SIGSTOP信号后,便由运行态进入停止状态,当收到SIGCONT信号时又会恢复运行态,该状态主要用于调试。
僵死状态(终止状态):进程已终止,但其task_struct 结构仍在内存中。顾名思义,处于这种状态的进程实际是死进程。

进程的状态转换:
这里写图片描述

进程ID

进程id PID, 父进程id PPID 每个进程都有一个非负整数表示的唯一进程ID,用于标志进程。

(一般为0~65535) 通过系统调用getpid();getppid();函数来获取
进程可以通过调用fork()函数来创建一个新进程,原来的进程称为父进程,新进程称为子进程。

fork与vfork

fork

fork从已存在的进程中创建新的进程。新进程为子进程,原进程为父进程。fork有两个返回值
父子进程共享代码,数据各自开辟空间,私有一份(采用写实拷贝)

fork()失败:内存不够,进程太多

pid_t fork(void);
//返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1

fork()函数被调用一次,但返回两次。其中父进程返回子进程的进程ID,
子进程返回0。子进程可以通过getppid()来获得父进程的进程ID。

vfork
vfork创建的子进程与父进程共享地址空间。

fork与vfork的区别

1.fork父子进程交替运行,vfork子进程运行,父进程阻塞,直到子进程结束
2.fork实现了写实拷贝,vfork就算写也不拷贝。
3.vfork必须用exit或excl
4.就算fork实现了写实拷贝,性能也没有vfork高
5.每个系统上的vfork都有问题,建议不要使用

僵尸进程和孤儿进程

僵尸进程:子进程结束,父进程还在,父进程没有接收到子进程的运行结果,并释放其占用的资源。

僵尸进程的主要危害:会一直占用进程ID,而进程ID是有限的。如果产生
大量的僵尸进程,将会导致进程ID耗尽。

//僵尸进程

int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return 1;
    }
    else if(id > 0) //父进程
    {
        printf("father process[%d] is sleeping..\n",getpid());
        sleep(30);
    }
    else
    {
        printf("child process[%d] is begin Z..",getpid());
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

孤儿进程:父进程提前退出,然后子进程退出,进入Z状态,子进程就称为孤儿进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
//孤儿进程
int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return 1;
    }
    else if(id  ==  0) //子进程
    {
        printf("I am child process..pid=[%d]\n",getpid());
        sleep(10);
    }
    else
    {
        printf("I am father process..pid=[%d]\n",getpid());
        sleep(5);
        exit(0);
    }
    return 0;
}

进程结束

正常结束:    1.main退出             退出码为0
            2.exit退出
不正常结束:  1.ctrl+c
            2.kill                退出码非0
            3.abort()

echo $?查看退出码
退出码范围:0~255int8个比特位,其余的位有别的用途,比如记录为何异常退出等情况

函数wait

函数原型:    
pid_t wait (int * status);    
参数说明:    
status:子进程的结束状态值会由参数status 返回    
pid_t:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中 

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
该函数我的理解是:为了避免出现僵尸进程(即父进程没有wait读取子进程的退出信息)

猜你喜欢

转载自blog.csdn.net/qq_40840459/article/details/80058705
今日推荐