Linux编程之进程

版权声明:非商业性使用,禁止演绎,转载请保留原文链接及作者 https://blog.csdn.net/qq_39478237/article/details/83189463

Linux下多任务介绍

首先,简单介绍下多任务系统,任务,进程,线程分别是什么?之间的区别是什么?从宏观角度理解后再针对每一个仔细探究

  • 什么叫多任务系统:多任务系统指可以同一时间内运行多个应用程序,每个应用程序被称作一个任务。

  • 任务定义:任务是一个逻辑概念,指由一个软件完成的任务,或者是一系列共同达到某一目的的操作。

  • 进程定义:进程是指一个具有独立功能的程序在某个数据集上的一次动态执行过程,它是系统进行资源分配和调度的最小单元。

  • 线程定义:线程是进程内独立的一条运行路线,是处理器调度的最小单元,也可以成为轻量级进程。

看了定义,还是不太理解,那就通俗的说一下它们的区别吧。
   ①通常一个任务是一个程序的一次执行,一个任务包含一个或多个完成独立功能的子任务,这个独立的子任务就是进程或线程。
   ②一个进程可以拥有多个线程,每个线程必须有一个父进程。

任务

任务是一个逻辑概念,指有一个软件完成的任务,或者由一系列共同达到某一目的的操作。通常一个任务是一个程序的一次执行,一个任务包含一个或多个完成独立功能的子任务,这个独立的子任务就是一个进程或线程。任务、进程、线程之间的关系如图

1

进程

进程的基本概念

进程是指一个具有独立功能的程序在某个数据集上的一次动态执行的过程,是系统进程资源分配和调度的基本单元。一次任务的运行可以并发激活多个进程,这些进程相互合作完成该任务的一个最终目标。 操作系统对进程的描述:PCB(进程控制块)Linux下的进程描述——task_struct

进程具有并发性、动态性、交互性、独立性、异步性等主要特性

进程和程序之间的区别:程序是一段代码,是一些保存在存储器上的指令有序集合,没有执行的概念;而进程是一个动态的概念,是程序执行的过程,包括动态创建、调度、消亡的整个过程,它是程序执行和资源管理的最小单位。

Linux下的进程结构

进程不但包括程序指令和数据,还包括程序计数器和处理器的所有寄存器及存储临时数据的进程堆栈,因此,正在执行的进程包括处理器当前的一切活动

内核将所有进程存放在双向循环链表(进程链表)中,其中链表的头是init_task描述符。链表的每一项都是类型为 task_struct,称为进程描述符的结构,该结构包含了一个进程相关的所有信息,定义在<include/linux/sched.h>文件中。task_struct内核结构比较大,它能完整的描述一个进程,如进程的状态、进程的基本信息、进程标识符、内存相关信息、父进程相关信息、与进程相关的终端信息、当前工作目录、打开的文件信息、所接收的信号信息等。

task_struct结构体中最重要的两个域:state(进程状态)和pid(进程标识符),下面就详细说说这两个

进程标识符

Linux内核通过唯一的进程标识符 PID 来标识每个进程(就和文件描述符一样)。PID存放在进程描述符的 pid 字段中,新创建的 PID 通常是前一个进程的 PID 加1,不过PID的值有上限(最大值=PID_MAX_DEFAULT-1,通常为32767),读者可以查看/proc/sys/kernel/pid_max 来确定该系统的进程数上限。

当系统启动后,内核通常作为某一个进程的代表。一个指向task_struct的宏current用来记录正在运行的进程。current经常作为进程描述符结构指针的形式出现在内核代码中,例如,current->pid 表示处理器正在执行的进程的PID。当系统需要查看所有的进程时,则调用for_each_process()宏,这将比系统搜索数组的速度要快的多。

查看进程

进程的信息可以通过/proc系统文件夹查看

  • 如:要获取PID为1 的进程,我们需要查看 /proc/1这个文件夹
  • 大多数进程信息同样可以使用top和ps这些用户级工具来获取
#include<stio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
    while(1)
    {
        sleep(1);
    }
    return 0;
}
  • 通过系统调用获取进程标识符
    在Linux中获得当前进程号的(PID)和父进程号(PPID)的系统调用函数分别为 getpid() 和 getppid()。

下面演示在Linux中获取进程pid

//getpid获取当前进程ID
//getppid获取父进程ID
//pid_t是C语言中用户自定义类型
//在sys/types.h中定义
//进程标识符演示

#include<stdio.h>
#include<unistd.h>
int  main()
{
    printf("pid:%d\n",getpid());
    printf("ppid:%d",getppid());
    return 0;
}

进程状态

Linux中的进程有以下几种状态:

● 运行状态(TASK_RUNNING):进程当前正在运行,或者正在运行队列中等待调度。

● 可中断的阻塞状态(TASK_INTERRUPTIBLE):进程处于阻塞(睡眠)状态,正在等待某些事件发生或能够占用某些资源。处在这种状态下的进程可以被信号中断。接收到信号或被显式的唤醒呼叫(如调用 wake_up 系列宏:wake_up、wake_up_interruptible等)唤醒之后,进程将转变为 TASK_RUNNING 状态。

● 不可中断的阻塞状态(TASK_UNINTERRUPTIBLE):此进程状态类似于可中断的阻塞状态(TASK_INTERRUPTIBLE),只是它不会处理信号,把信号传递到这种状态下的进程不能改变它的状态。在一些特定的情况下(进程必须等待,直到某些不能被中断的事件发生),这种状态是很有用的。只有在它所等待的事件发生时,进程才被显示的唤醒呼叫唤醒。

● 可终止的阻塞状态(TASK_KILLABLE):该状态的运行机制类似于TASK_UNINTERRUPTIBLE,只不过处在该状态下的进程可以响应致命信号。它可以替代有效但可能无法终止的不可中断的阻塞状态(TASK_UNINTERRUPTIBLE),以及易于唤醒但安全性欠佳的可中断的阻塞状态TASK_INTERRUPTIBLE)。

● 暂停状态(TASK_STOPPED):进程的执行被暂停,当进程收到 SIGSTOP、SIGSTP、SIGTTIN、SIGTTOU等信号时,就会进入暂停状态。

● 跟踪状态(TASK_TRACED):进程的执行被调试器暂停。当一个进程被另一个监控时(如调试器使用ptrace()系统调用监控测试程序),任何信号都可以把这个进程置于跟踪状态。

● 僵尸状态(EXIT_ZOMBIE):子进程先于父进程退出,它要保留退出原因在pcb中,因此退出后不会自动释放所有资源,子进程退出后操作系统通知父进程说子进程退出了,需要去获取原因,然后释放子进程资源。假如父进程不管子进程的退出状态,那么这个子进程将进入僵死状态,成为僵尸进程。

● 僵尸撤销状态(EXIT_DEAD):这是最终状态,父进程调用 wait 函数族“收尸”后,进程彻底由系统删除。

进程的创建、执行、终止

fork函数认识

  • 运行man fork
  • fork 有两个返回值,若直线成功,在父进程中返回子进程的pid,在子进程中返回0
  • 父子进程代码共享,数据各自开辟空间
#include<stdio.h>
#include<sys/typees>
#include<unistd.h>

int main()
{
    int ret = fork();
    printf("hello proc:%d,ret:%d\n",getpid(),ret);
    sleep(1);
    return 0;
}
  • fork 之后通常要用if分流
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>

int main()
{
    int ret = fork();
    if (ret < 0)
    {
        perror("fork");
        return 1;
    }
    else if(ret == 0)
    {
        printf("i am child:%d,ret:%d\n",getpid(),ret);
    }
    else
    {
        printf("i am father :%d,ret:%d\n",getpid(),ret);
    }
    sleep(1);
    return 0;
}

僵尸进程

//zombie.c
#include<stdio.h>
#include<unistd.h>
#include<errno.h>

int main()
{
    int pid = fork();
    if(pid < 0)
    {
        perror("fork error");
        return -1;    
    }else ifpid == 0)
    {
        printf("this is child\n");    
    }else{
        printf("this is parent\n");
    }
    while(1)
    {
        sleep(1);
    }
    return 0;
}

孤儿进程

父进程先于子进程退出,子进程将成为孤儿进程,当子进程成为孤儿进程时,Init进程将会回收,也就是说,父进程将变成init进程,init将负责释放资源


//orphan.c
#include<stdio.h>
#include<unistd.h>
#include<errno.h>

int main()
{
    int pid = fork();
    if(pid < 0)
    {
        return -1;    
    }else if(pid > 0)
    {
        printf("this is parent,%d\n",getpid());
    }
    printf("this is child,%d\n",getpid());        
    while(1)
    {
        sleep(1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39478237/article/details/83189463