初始Linux下进程及其创建

一.冯诺依曼体系
    这就是冯诺依曼体系。我们常见的笔记本,计算机,服务器大多数都是遵循冯诺依曼体系的,大体上它包含了输入输出设备,cpu,存储器三大组件。而这里的存储器我们一般指的是内存,并且在不考虑缓存的情况下,cpu只能对内存进行读写操作,不能访问外设,同样,外设要想输入或者输出数据,也只能向内存中进行读写,不能够访问cpu。总之,一切的设备都只能和内存打交道。我们的程序同样也是如此。当我们写好一个程序放在辅助存储器中的时候,cpu想要执行它,同样是先靠内存访问读取这个程序,然后再由内存交给cpu进行执行。
二.并发
    我们知道,cpu在同一个时间只能做一件事情,在早期的cpu设计的时候,cpu只能在同一时间执行一个程序,因此程序只能一个执行完以后,才能接着执行下一个程序,我们称这样的设计为单道程序设计。
   而现在我们在使用电脑的时候,为什么就可以一边打游戏,一边听歌,一边聊天呢,这就是因为采用了时钟中断技术,也就是多个程序的并发执行,每个程序只执行一段时间,接着cpu中断执行下一个程序,每个程序只占用cpu一段时间,而这个时间往往我们肉眼观察不出来,所以我们就觉着是多个程序在同时执行,这种设计模式我们称为多道程序设计。这样就能达到多个程序的并发执行了。
三.进程的概念
 1.程序:我们知道,程序就是完成特定任务的一系列指令集合。它包括了数据和代码
 2.进程:进程的定义:
①正在执行的程序;
②正在计算机上执行的实例;
③能分配给处理器并由处理器可以执行的实体。
④具有以下特征的活动单元:
一组指令序列的执行、一个当前状态和相关的系统资源。
进程的两个基本元素是:程序代码(可能被执行相同程序的其他进程共享—–>因为是只读的)和代码相关联的数据集。
可以说:进程是由程序代码和相关数据.堆栈还有程序控制块组成。
从一个用户的角度来看,进程就是程序的一次动态的执行过程。
从操作系统的角度来看,进程是操作系统分配资源的最小单位。
我们来总结一下程序和进程的区别:
1.程序是静态的,而进程是动态的。
2.进程的生命周期很短暂,而程序确实永久的。
3.进程有一个很重要的数据结构PCB来保存他的信息
4.一个进程只能对应一个程序,而一个程序可以对应多个进程。
而在计算机内部,一个个的进程都有自己独立的状态地址空间,是由一个带头节点的单链表串起来的,当我们需要并发执行下一个进程时候,就通过链表找到下一个进程即可。
在Linux下,PCB是一个叫做task_struct的结构体,我们着重讨论linux下的PCB。
究竟task_struct里面保存着那些进程的信息呢,我们可以查看linux2.6.38.8/include/linux/sched.h下的文件来观察。
1.进程状态,记录进程在等待,运行,或死锁
2.调度信息,由哪个调度函数调度,怎样调度等
3.进程的通讯状态
4.因为要插入进程树,必须有联系父子兄弟的指针,当然是tast_struct型
5.时间信息,比如计算好执行的时间 以便cpu分配
6.标号,决定进程归属
7.可以读写打开的一些文件信息
8.进程上下文和内核上下文
9.处理器上下文
10.内存信息
//---------------------------------------------------进程描述符结构定义---------------------------------------------------

struct task_struct
{
//---------------------------------------------------------进程状态------------------------------------------------------------ 
  long                  state;           //任务的运行状态


//---------------------------------------------------------进程标识信息--------------------------------------------------------- 
  pid_t                 pid;  //进程ID
  pid_t                 pgrp; //进程组标识,表示进程所属的进程组,等于进程组的领头进程的pid
  pid_t                 tgid;  //进程所在线程组的ID,等于线程组的领头线程的pid,getpid()系统调用返回tgid值。 
  pid_t                 session; //进程的登录会话标识,等于登录会话领头进程的pid。 
  struct pid            pids[PIDTYPE_MAX]; //PIDTYPE_MAX=4,一共4个hash表。
  char                  comm[TASK_COMM_LEN];  //记录进程的名字,即进程正在运行的可执行文件名 
  int                   leader; //标志,表示进程是否为会话主管(会话领头进程)。



//-------------------------------------------------------进程调度相关信息-------------------------------------------------------
  long                          nice;//进程的初始优先级,范围[-20,+19],默认0,nice值越大优先级越低,分配的时间片
  //可能越少。
  int                           static_prio;//静态优先级。
  int                           prio;//存放调度程序要用到的优先级。
    /*
    0-99 -> Realtime process
    100-140 -> Normal process
  */
  unsigned int                  rt_priority;//实时优先级,默认情况下范围[0,99]
    /*
    0 -> normal
    1-99 -> realtime
    */
  unsigned long                 sleep_avg;//这个字段的值用来支持调度程序对进程的类型(I/O消耗型 or CPU消耗型)进行
  //判断,值越大表示睡眠的时候更多,更趋向于I/O消耗型,反之,更趋向于CPU消耗型。
  unsigned long                 sleep_time;//进程的睡眠时间   
  unsigned int                  time_slice;//进程剩余时间片,当一个任务的时间片用完之后,要根据任务的静态优先级
  //static_prio重新计算时间片。task_timeslice()为给定的任务返回一个新的时间片。对于交互性强的进程,时间片用完之后,它
  //会被再放到活动数组而不是过期数组,该逻辑在scheduler_tick()中实现。
#if defined(CONFIG_SCHEDSTATS)||define(CONFIG_TASK_DELAY_ACCT)  
  unsigned int                  policy;//表示该进程的进程调度策略。调度策略有:
//SCHED_NORMAL 0, 非实时进程, 用基于优先权的轮转法。
//SCHED_FIFO 1, 实时进程, 用先进先出算法。
//SCHED_RR 2, 实时进程, 用基于优先权的轮转法
#endif
  struct list_head              tasks;//任务队列,通过这个寄宿于PCB(task_struct)中的字段构成的双向循环链表将宿主
  //PCB链接起来。
  struct list_head              run_list;//该进程所在的运行队列。这个队列有一个与之对应的优先级k,所有位于这个队列中
  //的进程的优先级都是k,这些k优先级进程之间使用轮转法进行调度。k的取值是0~139。这个位于宿主PCB中的struct list_head类
  //型的run_list字段将构成一个优先级为k的双向循环链表,像一条细细的绳子一样,将所有优先级为k的处于可运行状态的进程的
  //PCB(task_struct)链接起来。
  prio_array_t                  *array; //typedef struct prio_array prio_array_t; 可以说,这个指针包含了操作
  //系统现有的所有按PCB的优先级进行整理了的PCB的信息。 


//---------------------------------------------------------进程链接信息---------------------------------------------------------
  struct task_struct            *real_parent;//指向创建了该进程的进程的进程描述符,如果父进程不再存在,就指向进程
  //1(init)的进程描述符。
  struct task_struct            *parent;//recipient of SIGCHLD, wait4() reports. parent是该进程现在的父进程,
  //有可能是“继父”
  struct list_head              children;//list of my children.  children指的是该进程孩子的链表,使用
  //list_for_each和list_entry,可以得到所有孩子的进程描述符。  
  struct lsit_head              sibling;//linkage in my parent's children list.
//sibling为该进程的兄弟的链表,也就是其父亲的所有孩子的链表。用法与children相似。  
  struct task_struct            *group_leader;//threadgroup leader,主线程描述符
  struct list_head              thread_group;  //线程组链表,也就是该进程所有线程的链表。

//----------------------------------------------------------......------------------------------------------------------------

};
四.进程的状态:
在经典中,进程的状态分为就绪态,运行态,阻塞态,死亡态。
1.就绪态:进程的已经具备了运行条件,并没有被cpu调度过去。
2.运行态:进程被调度函数调度到cou中正在运行.
3.阻塞态:当cpu遇到中断指令,进程停止正在运行的状态,往往是I/O请求,当中断完成之后,进程由阻塞态变成就绪态。
4.死亡态:进程运行结束,就进入了死亡态。
就绪态---->执行态:进程被调度函数调度到cpu中运行。
执行态---->就绪态:当一个进程的时间片用完,需要执行下一个程序的时候.
执行态---->阻塞态:cpu发生中断通常是I/O请求
阻塞态---->就绪态:当中断结束,I/O完成的时候。
运行态----->死亡态:进程被执行完成
注意:无法从就绪态到阻塞态,同样无法从阻塞态到执行态。
在Llinux下面,我们将进程的状态分为7种。
1.R运行状态(running):并不意味着进程一定在运行中,它表明进程要么在运行中要么在运行队列里面。
2.S睡眠状态(sleeping):意味着进程正在等待事件完成(也叫可中断睡眠)
3.D磁盘休眠状态(Disk sleep)有时候也叫做不可中断睡眠,在这个状态的进程通常会等待io的结束。
4.T停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。
5.X死亡状态(dead):这个状态只是一个返回状态,你并不会在任务列表里面看见这个状态。

五.进程的查看
在linux下,进程的信息可以通过/proc系统文件查看
同样我们也可以通过指令来查看进程信息
1.ps 查看当前进程信息
2.ps -ef 查看所有进程信息
通过上面的叙述,我想读者对于这些信息并不陌生,PID就表示进程的编号,PPID表示父进程.
六.进程的创建
在linux下面我们可以通过系统函数fork()来创建一个进程。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)

{
printf("before fork(), pid = %d\n", getpid());
pid_t p1 = fork();
if( p1 == 0 )
{
printf("in child 1, pid = %d\n", getpid());
return 0; //若此处没有return 0 p1 进程也会执行 pid_t p2=fork()语句
}
pid_t p2 = fork();
if( p2 == 0 )
{
printf("in child 2, pid = %d\n", getpid());
return 0; //子进程结束,跳回父进程
Printf("hello world\");//没有打印
}
七.linux下的init进程
      init是Linux系统操作中不可缺少的程序之一。所谓的init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。所以,init始终是第一个进程(其进程编号始终为1)。
  内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。
以下是init的几个运行级别:
       # 0 - 停机(千万不能把initdefault 设置为0 )

  # 1 - 单用户模式

  # 2 - 多用户,没有 NFS

  # 3 - 完全多用户模式(标准的运行级)

  # 4 - 没有用到

  # 5 - X11 (xwindow)

  # 6 - 重新启动 (千万不要把initdefault 设置为6 )
当我们使用shutdown 关机时候,实际上调用的就是init  0关闭进程,在底层使用halt进行关机。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值