linux系统编程--进程概念与创建(三)

本文详细介绍了Linux系统中的进程概念,包括进程与程序的区别、线程、进程标识、进程结构以及不同状态。同时,讨论了进程的内存映像,如代码段、数据段、堆栈段的组成,并阐述了进程创建的过程,特别是fork函数在创建子进程中的作用。最后,通过示例解释了如何通过fork函数判断当前进程是父进程还是子进程。
概念

多用户多任务的操作系统,必须对程序能够并行执行。
核心的概念就是进程,或者说进程是操作系统资源管理的最小单位。
操作系统通过进程来管理计算机的软,硬件资源。

进程和程序的区别: 进程是动态的实体,是程序的一次执行过程;程序是静态的,是保存在硬盘上的代码。

线程: 在进程内又划分了许多线程,它是比进程更小的,能独立运行的基本单位。它与同一个进程中的其他线程共享进程的全部资源。

进程在执行过程中拥有独立的内存空间,而进程所属的内部线程共享这些内存。

一个线程可以创建或者撤销另一个线程,同一个进程中的多个线程可以并行执行。

在linux下可以使用ps或者pstree来查看当前系统中的进程。

进程标识

linux中,每个进程都有一个唯一的进程ID标识,进程ID是一个非负数,每个进程除了进程ID之外,还有一些其他的标识信息。
在unistd.h头文件中:

函数声明功能
getpid(id)获得进程ID
getppid(id)获得进程的父进程ID
getuid()获得进程的实际用户ID
geteuid()获得进程的有效用户ID
getgid()获得进程的实际组ID
getegid(id)获得进程的有效组ID
进程结构

一个进程由3个部分组成:代码段,数据段和堆栈段。
代码段存放程序的可执行代码;数据段存放程序的全局变量、常量、静态变量; 堆用来存放动态分配的变量;栈用来存放函数调用,如函数的参数、函数内部的局部变量。

进程的有以下几种状态:
①运行状态(R): 进程正在运行或在运行队列中等待运行。

②可中断等待状态(S):进程正在等待某个事件完成(如等待数据到达),等待过程可以被信号或定时器唤醒。

③不可中断等待状态(D):进程正在等待某个事件完成,但不能被信号或定时器唤醒,必须等待到事件完成。

④僵死状态(Z):进程已终止,但进程描述符依然存在,直到父进程调用wait()函数后释放。

⑤停止状态(T):进程因为收到SIGSTIP、SIGSTP、SIGTIN、SIGTOU信号后停止运行或者该进程正在被跟踪(调试程序时,进程处于被跟踪状态)。

用ps命令来查看进程的一些信息
在这里插入图片描述
还有一些后缀字符 PID为1的进程,大S为中断等待状态,小s说明该进程为会话首进程。
大I 说明是多线程的进程
<符号说明是高优先级进程,同样N代表低优先级进程,L代表内存锁页,即页不可以被换出内存。

在这里插入图片描述
在本机上的执行结果也说明,进程ID不一定就是连续的。

进程的内存映像

程序转化为进程:
①内核把可执行程序读入内存(复制),为程序分配内存空间。

②内核为该程序分配进程标识符(PID)和其他所需资源。

③内核为该程序保存PID及相应的状态信息,把程序放进运行队列中等待执行。

当内核把可执行程序读入内存后,一般布局如图所示:
在这里插入图片描述
代码段:即二进制机器代码,代码段是只读的,可以被多个进程共享。如果一个进程创建了一个子进程,父子进程共享代码段,此外子进程还获得父进程数据段、堆、栈的复制(不共享)。

数据段:存储已被初始化的变量,包括全局变量和已初始化的静态变量。

未初始化数据段:存储未被初始化的静态变量,也称为bss段。

堆: 动态分配的变量。

栈:用于函数调用,保持函数的返回值,函数的参数、函数内部的局部变量。

创建进程

创建进程有两种方式,一是由操作系统创建;二是由父进程创建。

由操作系统创建的进程,它们之间是平等的,没有继承关系。
由父进程创建的进程,它是子进程,子进程又可以创建进程,形成一个进程家族。

fork函数是创建一个新进程的唯一方法,这样创建出的进程自然就是子进程。

当创建了一个子进程后,父进程和子进程争夺cpu,抢到cpu者执行,另一个挂起等待。
在这里插入图片描述
#include <unistd.h> pid_t fork(void);
一般情况下,函数最多有一个返回值,但fork函数非常特殊,它有两个返回值。
成功调用fork后,当前进程实际上分裂为两个进程,一个返回值是创建的子进程ID,另一个返回值是0.


#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>


void error_exit(const char * pname);


int main()
{
    pid_t pid;

    pid=fork();

    if(pid==-1)
      { 
          error_exit("open:");
      }

    if(pid==0)
    {
      printf("succes create child process\n");
    }
    else
    {
      printf("the child process pid is :%d\n",pid);
    }
      
       
    return 0;
}
void error_exit(const char * pname)
{
    perror(pname);
    exit(1);
}

在这里插入图片描述
if语句只写了一次,但实际运行了两次,这就是因为父进程运行一次,子进程运行一次。
通过返回值,就能区分到底是哪个进程在运行了,返回值为0,代表子进程在运行。如果返回值大于0,代表父进程在运行。


#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void error_exit(const char * pname);


int main()
{
    pid_t pid;

    pid=fork();

    if(pid==-1)
      { 
          error_exit("open:");
      }

    if(pid==0)
    {
      //printf("succes create child process \n");
      printf("child process pid is running %d\n",getpid());

    }
    else
    {
      printf("the child process pid is :%d\n",pid);
      printf("parent process pid is running %d\n",getpid());
    }
    
      
    return 0;
}
void error_exit(const char * pname)
{
    perror(pname);
    exit(1);
}

注意这里有个弯绕,父进程在运行时,pid是子进程的ID,但是getpid()是当前进程的ID,也即父进程ID

我的运行结果都是父进程先运行,但具体谁先运行时不确定的,取决于内核所使用的调度算法。
在这里插入图片描述
把判断改成switch格式


#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>


void error_exit(const char * pname);


int main()
{
    pid_t pid;

    pid=fork();
    switch (pid)
    {
    case -1:
      
        error_exit("process");
        break;
      

   case 0:
   
        printf("child process pid is running %d\n",getpid());
        break;
   
  default:
    
        printf("parent process pid is running %d\n",getpid());
        break;
  
    }

    
          
    return 0;
}
void error_exit(const char * pname)
{
    perror(pname);
    exit(1);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值