当前位置: 首页 > news >正文

Linux第四章 进程

4.1 前言

本章讨论进程概念、资源、属性。

4.2 内核和进程的关系

当系统启动时,内核代码被加载到内存,初始化之后,启动第一个用户进程,然后内核的代码就等着用户进程来调度了。

4.3 进程是程序的实例

当程序员编写好一个程序,编译之后会生成这个可执行程序,这个程序可以被运行。

运行程序其实是用户进程(Shell进程)指示内核要启动另一个用户进程,内核便为这个新的进程分配资源,并加载该进程的代码和数据。

一个程序可以被运行多次。

4.3 进程资源

4.3.1 PCB

进程运行时,内核为进程每个进程分配一个PCB(进程控制块),描述进程的信息。

PCB在内核中对应的结构体是task_struct

4.3.2 虚拟地址空间

每个进程都会分配虚拟地址空间,在32位机器上,该地址空间为4G。

更细节的图例

在进程里平时所说的指针变量,保存的就是虚拟地址。当应用程序使用虚拟地址访问内存时,处理器(CPU)会将其转化成物理地址。

int* p = malloc(100);
*p = 100;
访问内存时,系统会做地址转换。

这样做的好处在于:

  • 进程隔离,更好的保护系统安全运行

  • 屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址

思考:如果实现一个智能的myfree函数,该函数会自动判断指针是否在堆上还是在栈上,还是在全局变量中。

4.3.3 CPU

CPU的分配是动态的,不是进程一加载就直接分配的,一般来说每个系统都会有许多进程同时在运行,而CPU只有一个(多核CPU可以认为是多个,但是数量远少于进程数量)。那么,进程就需要排队等待,就好像有100个人,在4个卖饭的窗口买饭一样。

内核将进程PCB放入一个队列,总是让CPU服务队列中的第一个进程,服务时间可以是10毫秒,可以是25毫秒,具体多长时间跟具体系统有关系,这个时间有个名字叫做时间片。一旦这个进程服务时间到,这个进程会被丢到队列尾部,进行排队。进程调度。

内核中有一个常量HZ,一般是100,250, 1000

4.4 进程属性和状态

进程有许多的属性和状态,具体可以看task_struct,这里挑一些常见的进行讲解。

4.4.1 PID

进程编号,内核为每个进程分配一个进程编号,这个是进程的身份证,系统保证了不会重复分配。
通过函数getpid或者命令ps可以查看进程的PID。

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

int main()
{
    pid_t pid = getpid();
    pid_t ppid = getppid();
    printf("%d\n", (int)pid);
    printf("%d, %d\n", (int)pid, (int)ppid);
}

4.4.2 PPID

PPID就是父进程ID,在Linux系统中,除了内核启动的第一个进程,其它进程都有父进程。
通过函数getppid或者命令ps可以查看进程的PPID。

4.4.3 账户ID/组ID

账户分实际账户和有效账户两种,如果你使用test账户登陆系统,但是使用sudo运行程序时,实际账户时test,有效账户时root。

通过函数getuidgeteuid获取真实账户id和有效账户id
通过函数getgidgetegid获得真实账户id和有效账户id
通过setuidsetgidseteuidsetegidsetreuidsetregid等设置进程的有效和真实账户id。

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

int main()
{
    uid_t uid = getuid();
    uid_t euid = geteuid();
    printf("uid =%d, euid=%d\n", (int)uid, (int)euid);
}

 

4.4.4 进程组ID/会话组ID/控制终端

进程组:getpgrpsetpgid
会话组:getsidsetsid
控制终端:

4.4.5 环境变量

保存该进程运行的环境信息。
进程的环境变量保存在全局变量environ中,
也可以通过setenvgetenvunsetenv进行设置和获取。

4.4.6 进程状态

#define TASK_RUNNING            0 可运行状态,相当于进程三种状态的执行和就绪状态
#define TASK_INTERRUPTIBLE      1 中断等待状态。处于这种状态唤醒的原因可能是信号或定时中断,或者I\O就绪
#define TASK_UNINTERRUPTIBLE    2 不可中断等待状态,主要是等待I\O
#define TASK_ZOMBIE             3 僵死状态,进程已经结束已经释放除了PCB以外的部分系统资源,等待父进程wait()读取结束状态
#define TASK_STOPPED            4 进程已经停止
注意:这是0.11的内核,在1.0的内核以上就多了一种状态,在1.0内核的sched.h中有定义
#define TASK_SWAPPING           5 交换状态,进程的页面也可以从内存转换到外存,以空出内存空间

启动进程时,该进程在RUNNING状态,RUNNING状态的进程有可能时正在被执行,或者在队列中排队。但是如果进程调用阻塞函数,而运行条件不满足时,该进程会进入挂起状态。挂起状态的进程不再分配CPU,除非等到运行条件满足时。会阻塞进程运行的函数有许多,比如getchar是典型的阻塞调用。

阻塞函数列表可以在man 7 signal中,找到关于阻塞函数的列表。

 

4.4.7 文件描述符

在进程控制块中,有一个数组保存着打开的文件描述符信息。

4.4.8 进程时间

进程有一些字段,用来记录进程的运行时间。
通过times可以获取进程从运行开始时到执行times函数时,所花费的时间。这个在系统性能优化时特别重要。
简单的程序可以从time命令获取进程的运行时间。
Linux时间相关函数可以从man 7 time获取。

 

4.4.10 当前工作目录和根目录

当前工作目录是相对地址的相对目录,通过getcwd函数可以获取当前目录,也可以通过pwd或者echo $PWD获取。也可以通过chdir来修改当前工作目录。
根目录是绝对地址的相对目录,可以通过chroot来修改根目录。调用chroot需要root权限。

目录相关资料在man 7 path_resolution

4.5 动态库和静态库

当使用动态库时,系统会检查该动态库是否已经加载,如果已经加载,则直接映射即可,如果没有加载,那么会加载之后再映射。

如果动态库中有全局变量,那么该全局变量对于不同的进程来说,是相互独立和隔离的。

链接静态库时,静态库被一起编译进可执行程序,运行时不再依赖静态库。

动态库编译:
gcc -fpic -shared a.c b.c -o libtest.so
链接动态库
gcc main.c -ltest -L. -o mybin
运行程序时

export LD_LIBRARY_PATH=.
或者将动态库拷贝到/usr/lib
./mybin

静态库打包:
ar rcs libtest.a a.o b.o

链接库时,如果有同名的动态库和静态库,默认优先动态库,如果要链接静态库,那么使用-static,比如

gcc a.c -lmylib -static

通过以下方式可以指定某些库使用静态链接,而某些库使用动态链接

-Wl,-Bstatic -ltest -Wl,-Bdynamic -ltest2

4.6 内存管理

进程运行时,总是占用内存,无论是加载代码,还是在函数中定义局部变量,还是调用malloc申请内存。

无论是那种原因,进程需要使用内存时,它将向系统申请,并获得相对应的虚拟地址,而进程只能访问虚拟地址,真实的内存地址,进程无法访问。当进程访问虚拟地址时,系统会负责进行虚拟地址到物理地址的转换,系统发现进程尝试访问非法地址,那么进程将得到惩罚(段错误)。

这样做保护了系统的稳定性,不会因为个别新手程序员导致整个系统的崩溃。

另外还有一个好处是,使用虚拟内存之后,每个进程的导致空间是一致的,简化了进程的设计。

相关函数:mallocbrkmmapalloca

4.7 进程总结

从用户的角度看,一个程序跑起来就是进程。而从操作系统的角度看,进程是一个控制块+代码+数据的组合。

4.8 函数和命令

4.8.1 函数

getpid:获取进程ID
getppid:获取父进程ID

getuid:获取实际用户ID
getgid:获取实际组ID
geteuid:获取有效账户ID
getegid:获取有效组ID

进程组描述了一项任务
getpgrp:获取进程组号
setpgid:设置进程组号

setsid:设置Session号
getsid:获得Session号

getcwd:获取当前工作目录
chdir:设置当前工作目录
chroot:修改当前根目录

getenv:环境中取字符串,获取环境变量的值
setenv:改变或增加环境变量
unsetenv
extern char** environ(全局变量)

malloc/free:堆区申请内存
mmap/munmap:在映射区申请内存
brk:全局区申请内存
alloca:在栈上申请内存

int foo(int len)
{
//  char buf[len];
    char* buf = alloca(len);
}

4.8.2 命令

ps axu:现行终端机下的所有程序,以用户为主的格式来显示程序状况,显示所有程序,不以终端机来区分
ps ajx
grep:搜索
kill:杀死进程(给进程发送信号)

转载于:https://www.cnblogs.com/w-x-me/p/6399257.html

相关文章:

  • css选择器有哪些
  • Hbase备份
  • 前端战五渣学前端——初探Parcel急速打包
  • Android程序员搞Web之HTNL(一)
  • mysql-proxy安装过程 (转)
  • Day3LJY
  • angular 摇树优化
  • EOS 坑 右击java文件封装成Web Service不弹界面
  • ./configure、make、make install 命令
  • div浮动+盒子模型+溢出隐藏+滚动条
  • JavaScript创建对象的四种方式
  • HttpServletResponse应用(转)
  • 体育竞技游戏的团队AI
  • 1-思科防火墙:搭建防火墙环境
  • 棋盘问题 dfs()基本的
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • Angular 2 DI - IoC DI - 1
  • es6(二):字符串的扩展
  • Intervention/image 图片处理扩展包的安装和使用
  • Java,console输出实时的转向GUI textbox
  • Javascript设计模式学习之Observer(观察者)模式
  • Linux中的硬链接与软链接
  • miaov-React 最佳入门
  • passportjs 源码分析
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • Zepto.js源码学习之二
  • 前端之Sass/Scss实战笔记
  • 如何设计一个比特币钱包服务
  • 三分钟教你同步 Visual Studio Code 设置
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 你对linux中grep命令知道多少?
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • ​2021半年盘点,不想你错过的重磅新书
  • ​linux启动进程的方式
  • #pragma 指令
  • #微信小程序(布局、渲染层基础知识)
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (12)Hive调优——count distinct去重优化
  • (二)丶RabbitMQ的六大核心
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (附源码)计算机毕业设计SSM智慧停车系统
  • .describe() python_Python-Win32com-Excel
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .NET 回调、接口回调、 委托
  • .NET 设计一套高性能的弱事件机制
  • @JsonSerialize注解的使用
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • @selector(..)警告提示
  • []T 还是 []*T, 这是一个问题
  • [Angular 基础] - 数据绑定(databinding)
  • [Bzoj4722]由乃(线段树好题)(倍增处理模数小快速幂)
  • [C#]C# OpenVINO部署yolov8图像分类模型