操作系统和进程入门

进程

什么是进程

可执行程序运行时加载到内存中的程序是进程
进程是运行在计算机中的,那么必然在计算机层面上进程是什么?
进程究竟是什么,由什么组成呢?

操作系统必然要管理多个加载到内存中的程序
那么如何管理这些程序呢?
先描述,再组织

那么进程的组成就在于是如何去描述这些进程的
那么通过什么来描述对象呢,当然是对象的属性
在c中使用结构体通过列举对象的属性来描述对象,
因为对对象作管理实际上也就是对对象的属性进行管理

struct XXX{
	//状态
    //优先级
    //内存指针字段
    //标识符
    //... 包含进程几乎所有的属性字段
};

该结构体实际上就是PCB(process ctrl block)进程控制块

当可执行程序被执行时,代码和数据会被加载到内存中,同时也会构建一个PCB,并且让PCB中的内存指针字段指向内存中的代码和数据

那么为什么构建一个PCB对象呢?因为操作系统要进行管理,要管理就要描述,PCB就是对一个进程的描述,然后把各个对象通过使用某个数据结构对对象进行组织,最后把对对象的管理操作转化为对数据结构的增删查改操作

进程 = 内核PCB对象 + 可执行程序

可能不止用一个类来描述进程,则

进程 = 内核数据结构 + 可执行程序

所有对进程的控制和操作,都只和进程的PCB有关,和进程的可执行程序没有关系

在这里插入图片描述


task_struct—PCB的一种

  • 在Linux中描述进程的结构体(PCB)叫做task_struct
  • task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
  • task_struct是操作系统内部的数据,是由操作系统管理的

task_struct(PCB)内部属性内容(粗粒度)

  • 标识符:描述本进程的唯一标识符,用来区分各个进程
  • 状态:任务状态,退出代码,退出信号等,即当前进程所处状态
  • 优先级:相对于其他进程的优先级
  • 程序计数器:该进程的程序中即将被执行的下一条指令的地址
  • 内存指针:指向程序的代码和与进程相关的数据,还有指向和其他进程共享的内存块的指针
  • 上下文数据(后面学进程切换时详细探讨)
  • I/O状态信息:该进程拥有的(I/O)设备字段或信息,例如要显式器打印数据时,需要通过该信息让操作系统知道你需要显式器的使用权
  • 记账信息:可能包括使用处理器的时间总和,使用的时钟数总和,时间限制,记账号等
    例如用来判断一个进程使用处理器的时间,如果时间很长了,表明不能够再让你使用
    即让操作系统通过该信息来做出正确的决策
  • 其他信息

创建进程的细节

当一个可执行程序被执行开始创建进程时,究竟是先把代码和数据写入到内存中,还是先创建PCB呢?

如果是先把代码和数据写入内存,那么对于一个几十G的程序体积非常的程序而言,内存根本不能够一次性加载完所有数据,则需要批量加载,但是先加载了多少呢,还有多少没有被加载呢,当前进程应不应该被调度呢,如果没有PCB,则这些信息都无法知晓,所以创建一个进程时,PCB被最先创建,并且在PCB还未创建成功时,代码和数据都不会被加载到内存,只有当PCB被创建成功后,代码和数据才会被加载

查看进程

使用ps ajx查看进程
使用while :; do ps ajx | head -1 && ps axj | grep myprocess | grep -v grep;sleep 3; done
来循环查看捕捉的进程信息

如何获得进程id信息

当想要获取内核(操作系统内部)的数据,只能通过调用系统调用函数(接口)
那么要获得当前进程的id和父进程的id则需要调用getpid和getppid系统调用函数

在这里插入图片描述

bash父进程

在这里插入图片描述

在这里插入图片描述

每一次启动该程序,pid都不同,因为每次都是一个新的进程,但是ppid(父进程id)一直保持不变,查看后,发现该进程号所对应的进程就是Bash,所以在命令行启动的程序转化为进程后,都是Bash子进程,相当于每次都让自己的子进程去做事,如果有问题发生也保证了自己的正常运行,为用户提供了良好的稳定的使用环境


进程文件

Linux中一切皆文件,进程也是文件形式

进程文件目录 : /proc

当程序运行后,进程以文件的形式被创建在/proc目录下,当进程被结束,该进程文件也就销毁

文件内容

dr-xr-xr-x 2 tck tck 0 Apr  9 08:43 attr
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 autogroup
-r-------- 1 tck tck 0 Apr  9 08:43 auxv
-r--r--r-- 1 tck tck 0 Apr  9 08:43 cgroup
--w------- 1 tck tck 0 Apr  9 08:43 clear_refs
-r--r--r-- 1 tck tck 0 Apr  9 08:39 cmdline
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 comm
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 coredump_filter
-r--r--r-- 1 tck tck 0 Apr  9 08:43 cpuset
lrwxrwxrwx 1 tck tck 0 Apr  9 08:43 cwd -> /home/tck/study/process_2023-11-19
-r-------- 1 tck tck 0 Apr  9 08:43 environ
lrwxrwxrwx 1 tck tck 0 Apr  9 08:43 exe -> /home/tck/study/process_2023-11-19/myprocess
dr-x------ 2 tck tck 0 Apr  9 08:43 fd
dr-x------ 2 tck tck 0 Apr  9 08:43 fdinfo
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 gid_map
-r-------- 1 tck tck 0 Apr  9 08:43 io
-r--r--r-- 1 tck tck 0 Apr  9 08:43 limits
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 loginuid
dr-x------ 2 tck tck 0 Apr  9 08:43 map_files
-r--r--r-- 1 tck tck 0 Apr  9 08:43 maps
-rw------- 1 tck tck 0 Apr  9 08:43 mem
-r--r--r-- 1 tck tck 0 Apr  9 08:43 mountinfo
-r--r--r-- 1 tck tck 0 Apr  9 08:43 mounts
-r-------- 1 tck tck 0 Apr  9 08:43 mountstats
dr-xr-xr-x 6 tck tck 0 Apr  9 08:43 net
dr-x--x--x 2 tck tck 0 Apr  9 08:43 ns
-r--r--r-- 1 tck tck 0 Apr  9 08:43 numa_maps
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 oom_adj
-r--r--r-- 1 tck tck 0 Apr  9 08:43 oom_score
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 oom_score_adj
-r--r--r-- 1 tck tck 0 Apr  9 08:43 pagemap
-r-------- 1 tck tck 0 Apr  9 08:43 patch_state
-r--r--r-- 1 tck tck 0 Apr  9 08:43 personality
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 projid_map
lrwxrwxrwx 1 tck tck 0 Apr  9 08:43 root -> /
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 sched
-r--r--r-- 1 tck tck 0 Apr  9 08:43 schedstat
-r--r--r-- 1 tck tck 0 Apr  9 08:43 sessionid
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 setgroups
-r--r--r-- 1 tck tck 0 Apr  9 08:43 smaps
-r--r--r-- 1 tck tck 0 Apr  9 08:43 stack
-r--r--r-- 1 tck tck 0 Apr  9 08:41 stat
-r--r--r-- 1 tck tck 0 Apr  9 08:41 statm
-r--r--r-- 1 tck tck 0 Apr  9 08:39 status
-r--r--r-- 1 tck tck 0 Apr  9 08:43 syscall
dr-xr-xr-x 3 tck tck 0 Apr  9 08:42 task
-r--r--r-- 1 tck tck 0 Apr  9 08:43 timers
-rw-r--r-- 1 tck tck 0 Apr  9 08:43 uid_map
-r--r--r-- 1 tck tck 0 Apr  9 08:43 wchan
  • cwd为当前工作目录

cwd -> /home/tck/study/process_2023-11-19

文件操作:fopen(“file.txt”,“w”);//如果不存在这个文件,就会在当前路径下,帮我们创建一个文件
这里的当前路径就是cwd


  • 可通过chdir来改变当前进程的工作目录,即会更改文件创建的位置
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


  • exe为可执行程序的路径
    当删除原本的可执行程序时,进程依然会运行,因为代码和数据都加载到了内存里
    同时当进入/proc查看该进程时,exe也会标识指向的可执行程序文件已被删除

在这里插入图片描述

fork创建进程

在这里插入图片描述

作用:创建一个当前进程的子进程

在这里插入图片描述

  • 成功创建子进程,在父进程返回子进程的PID,在子进程返回0(任何进程的PID都大于0)
  • 创建失败,则返回-1给父进程,错误码被设置

此处由于返回值的不同,则可以区别父子进程,从而达到一定程度的进程控制
返回值大于0的则是父进程,返回值为0的则是子进程,用返回值小于0来查错

前面提到 进程 = 内核数据结构 + 代码和数据

  • fork之后父子共享内存中的代码
  • 但是数据父子各自开辟一份空间,采用写时拷贝

fork效果

在这里插入图片描述

在这里插入图片描述

fork之前父进程打印信息,fork之后父子进程打印信息子进程创建成功,这里的父进程3503的父进程2227就是bash,同时验证了返回值id

在这里插入图片描述

进程分支

为了达到让父子进程执行不同的逻辑,可以用前面提到的id不同,来进行条件判断,从而达到区分父子进程

#include <stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(){
    printf("before fork: I am a process, pid: %d, ppid: %d\n",getpid(),getppid());

    pid_t id = fork();
    if(id==0){//子进程
        while(1)
        {
            printf("after fork:我是子进程 I am a process, pid: %d, ppid: %d, id: %d\n",getpid(),getppid(),id);
            sleep(1);
        }
    }
    else if(id>0){//父进程
        while(1)
        {
            printf("after fork:我是子进程 I am a process, pid: %d, ppid: %d, id: %d\n",getpid(),getppid(),id);
            sleep(1);
        }
    }
    else{
        return -1;//id<0,子进程创建失败,父进程的id小于0,在此处返回
    }

    printf("after fork: I am a process, pid: %d, ppid: %d, id: %d\n",getpid(),getppid(),id);

    sleep(2);
    return 0;
}

在这里插入图片描述

可见对于父子进程进行了分支,pid为2227的是Bash

fork解析

  • 为什么给父进程返回子进程的pId,而给子进程返回0

一个父进程能够有多个子进程,为了唯一标识创建的子进程,则返回子进程id,而子进程有且只能有唯一一个父进程,可以直接调用系统调用函数得到父进程的pid则无需返回父进程的pid给子进程而返回0区分当前的进程是创建出来的子进程

  • fork之后的代码才是共享的,为什么fork函数一次会返回两个值

在一个函数内,返回值是最后执行的语句,函数的主要功能已经执行完后,才会执行返回操作,则当fork在返回的时候,fork的核心代码已经执行完毕,即已经创建出了子进程,然后在fork内给父进程返回子进程pid,然后给子进程返回0

  • 名字同为id的变量为什么能够接收两个不同的值

这里变量id在父进程和子进程的上下文中存在各自的实例,可以理解为写实拷贝,这里创建进程后直接拷贝写入了变量id,当进程切换时,会把原来进程存储在寄存器的上下文拿出来在逻辑上保存到和PCB与代码数据的一个位置放到一起,把要cpu运行的另一个进程的上下文重新存放进cpu的寄存器中

创建多个子进程并使用exit退出进程

在这里插入图片描述

exit:导致普通进程的终止,即退出子进程

进程(任意)之间是互相独立的,则在代码层面上控制进程的退出可使用封装好的exit函数,一个进程崩溃了不会影响其他进程,使得程序的生命力更加旺盛

  • 35
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值