Linux-(进程基础)学习笔记

  1. 进程的相关基本概念
    1.1 进程的定义
    站在用户角度 :进程是一个程序的一次执行过程,在这个过程中,伴随着资源的分配和释放。
    站在操作系统角度:进程是程序执行时相关资源的总称,当进程结束时所有的资源都会被系统自动回收。“进程是资源分配管理的最小单位。”
    1.2 进程与程序
  1. 程序(program):静态的,它是一些保存在磁盘上的指令的有序集合(可执行文件),没有任何执行的概念。
  2. 进程(process):动态的概念,它是程序执行的过程,包括创建、调度和消亡。在这个过程中,伴随着资源的分配和释放。
    1.2.2 联系
    1)程序(ELF可执行文件格式)
    一般由多个段section组成,主要有代码段(text)和数据段(data)。我们只关心主要的部分
    2)进程的内存分布
    物理内存: 内存条、运行内存(SDRAM、DDR5)
    32bit Linux操作系统采用虚拟内存管理技术:每个进程都有独立的地址空间。该地址空间的大小为4G(2^32)的线性虚拟空间

用户空间(0-3G)
代码段(text) : 二进制的代码、代码段是只读
数据段(data) : 已初始化的,全局变量或静态变量(static)
BSS段 : 未初始化的,全局变量或静态变量(static)
堆区(heap) : 内存分配和释放是由程序员所控制的(malloc, free).
栈区(stack) : 系统自动分配和释放.存储局部(自动)变量.
内核空间(3-4G)

3)进程控制块(task_struct 结构体)
内核会为每个进程创建一个进程描述符task_struct的数据结构对象(内核用于描述 和 管理进程所使用的数据结构)
task_struct 结构比较大,它能完整地描述一个进程,如进程的状态,进程的基本信息,当前的程序计数器值、CPU的寄存器值、打开的文件等等。
进程 = 4G虚拟空间 + task_struct

1.3 进程的标识(pid)
Linux内核通过唯一的进程标识符 PID(process identity number)来标识每一个进程。
系统中可以创建的进程数目有限(PID最大默认设置为32768),可以查看/proc/sys/kernel/pid_max来确定上限。

1.4 进程的类型
1)交互进程 : 由一个Shell启动的进程。这类进程经常与用户进行交互,所以一般优先级较高。
交互进程既可以在前台运行,也可以在后台运行。
2)批处理进程 :是一个进程序列,该进程负责按照顺序启动其它进程.这类进程不必与用户交互因此通常在后台运行。
3)系统守护进程: 该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才关闭

1.5 进程的状态
单核CPU 同一时刻只能运行一个进程(背景)
时间片轮询 每个进程都会得到一个时间片,时间片上的时间用尽之后,进程必须让出CPU
1)可运行态状态(R TASK-RUNNING): 此时进程正在运行(running),或者正在运行队列中等待调度(runnable)。
2)可中断睡眠态(S TASK_INTERRUPTIBLE): 此时进程正处于阻塞(睡眠)状态,正在等待某些事件发生或者能够申请某些资源。(可被信号改变状态)
在这种状态下的进程可以被信号中断。进程会在等待的事件发生或者是接收到信号被唤醒,进程转变为(TASK_RUNINNG状态)。
3)不可中断睡眠态(D TASK_UNINTERRUPTIBLE):与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。(不可被信号改变状态)
信号传递给这种状态下的进程不能改变它的状态。只有在它等待的事件发生时,进程才被显示的唤醒。
4)暂停态(T TASK_STOPPED):进程的执行被暂停,
5)僵尸态 (Z EXIT_ZOMBIE):处于这种状态下的进程已经放弃所有了几乎所有的资源
6)消亡态(X EXIT_DEAD):该进程将被彻底释放。所以EXIT_DEAD状态是非常短暂的,几乎不可能通过ps命令捕捉到。

1.6 进程的运行模式
用户模式(用户态): 进程工作在0-3G空间(CPU 工作在受限模式 普通模式)
|
| 中断(异常): 系统的中断
| 系统调用(system call):内核向用户提供的具有预定功能的函数接口(访问内核的接口)
|
内核模式(内核态):进程工作在3-4G空间(CPU 工作在受信任模式 特权模式)
2. 进程相关管理指令
2.1 启动进程
【1】 at 指令 (在指定的时刻执行相关的进程)
首先检查atd服务有无开启
ps -eLf | grep atd查看,

sudo dpkg -L at | grep at 检查是否安装
sudo apt-get install at 安装

如果没有开启则需要开启用
sudo /etc/init.d/atd start or restart

语法: at [参数] [时间]
at> 执行的指令
退出at命令 ctrl+d

时间格式:
(1)HH:MM YYYY-MM-DD 说明:规定在某年某月的某一天的特殊时刻进行该项任务
at 04:00 2009-03-17

查询删除当前待执行任务
查询当前的等待任务,被执行之后就不会显示
atq
删除系统中由at建立的正在等待被执行的任务

atrm 任务的工作号
例如: atrm 17

【2】 cron 指令 (周期性执行相关进程)
首先检查atd服务有无开启
ps -eLf | grep cron查看,

sudo dpkg -L cron | grep cron 检查是否安装
sudo apt-get install cron 安装

如果没有开启则需要开启用
sudo /etc/init.d/cron start or restart

语法:
crontab [可选项]
-u 用户名 //设定某个用户的cron服务,如果不指定用户默认为当前用户
-l //列出某个用户cron服务的详细内容
-r //删除某个用户的cron服务
-e //编辑某个用户的cron服务
第一使用:
Select an editor. To change later, run ‘select-editor’.

  1. /bin/ed
  2. /bin/nano <---- easiest
  3. /usr/bin/vim.basic
  4. /usr/bin/vim.tiny
    如果选错了:
    select-editor

指令格式
m h dom mon dow command
说明:# minute (m), hour (h), day of month (dom), month (mon), and day of week (dow)
第1列表示分钟0~59
第2列表示小时0~23(0表示0点)
第3列表示日期1~31
第4列表示月份1~12
第5列标识号星期0~7(0或7都表示星期天)
第6列要运行的命令
“*” 表示 any 任意时间
“-” 代表从某个数字到某个数字(表示范围)
“,” 代表分开几个离散的数字。
“/” 代表间隔的数字

0 23-7/2,9,10。。。 echo “Have a good dream” >> /tmp/test.txt
0 9,7,8,5。。。。echo “Have a good dream” >> /tmp/test.txt
0 22 。。。1-5 echo “It’s time to go home” >> /tmp/time.txt
0 5 。。。1 tar -zcf /var/backups/home.tgz /home/
2.2 进程相关指令
【1】ps (Process Status)指令
ps命令列出的是当前那些进程的快照
ps -aux

【2】top 指令 下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,
top -d time(seconds)
htop

【3】kill 向指定进程发送信号
kill -l 查看信号类型
语法:
kill -signalnum pid
如果不选择信号的类型默认情况下发送的是SIGTERM信号

【4】进程优先级
系统中运行的每个进程都有一个优先级(亦称“nice 值 谦让度”),其范围从 -20 (最高优先级)到 19 (最低优先级)。
优先级越高,其值越小。默认情况下,进程的优先级是 0 (“基本”调度优先级)。
ps -elf 或 top

1)nice 是按照指定的优先级执行命令
nice -n 优先级 command
例如
nice -n 6 ./a.out 以优先级6来运行a.out

2)renice更改正在运行的进程的优先级
renice 优先级 pid
例如
renice +10 9621 将9621进程的优先级改为10

注意:普通用户只能降低它们自己进程的优先级别,并限于 0 到 19 之间。超级用户(root)可以将任何进程的优先级设定为任何值。

【5】前后台管理指令

由shell启动交互进程,既可以放在前台也可以放置后台
前台进程:既可以从终端获取数据,也可以向终端输出数据
后台进程:只能向终端输出数据

  1. jobs:查看当前终端有多少在后台运行的命令
  2. command &:运行一个程序时,直接放到后台
  3. ctrl+c : 终止前台进程
  4. ctrl+z : 将一个正在前台执行的进程放到后台,并且暂停
  5. fg : 将后台中的进程调至前台继续运行,如果后台中有多个命令,可以用 fg N将选中的命令调出,N是通过jobs命令查到的后台正在执行的命令的序号[N] (不是pid)。
  6. bg : 将一个在后台暂停的命令,变成继续执行。

/dev/null 所有添加到该文件的数据 都会被自动丢弃

3.进程基础系统调用函数
【1】fork 创建进程
#include <unistd.h>
pid_t fork(void);
函数功能:
通过复制当前进程(parent 父进程)创建新进程(child 子进程)
返回值(一次调用两次返回):
成功
父进程 返回 子进程的 PID
子进程 返回 0
失败
失败 返回 -1

复制时复制了父进程的PC(程序计数器)
PPID (parent pid) 父进程号

pid_t getpid(void); Returns: process ID of calling process
pid_t getppid(void);Returns: parent process ID of calling process

注意:

  1. fork 之后 谁先运行谁后运行:
    我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的(schedule算法)实现。
  2. 谁先结束谁后结束:
    如果父进程,在子进程结束之前先结束。那么子进程就会变成"孤儿进程",随后 init 来接管他,成为他的父进程.
    如果子进程,在父进程结束前先结束,且父进程一直不退出,也不"收尸"。那么子进程就会变成"僵尸进程"。
    (如何处理僵尸进程将在wait时候详述)

【2】exec 函数族 (进程替换)
 exec函数族提供了一种在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,
并用它来取代原调用进程的数据段、代码段和堆栈段。在执行完之后,原调用进程的内容除了进程号外,其他全部都被替换了。

int execl(const char *path, const char *arg, …);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg, …, char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, …);
int execvp(const char *file, char *const argv[]);

函数参数:
[1]可执行文件查找方式:
路径名pathname:其他函数
文件名filename:(以p结尾的函数)可以只给出文件名,系统会自动从环境变量“$PATH”所包含的路径中进行查找。
[2]向新程序传递参数表(命令行参数)的方式 (注意这些参数必须以NULL结尾。)
列表方式list:将命令行参数逐个传递给函数,五位字母来区分,字母为“l”(list)的表示:逐个列举的方式
矢量数组方式vector(指针数组):将所有参数放入一个指针数组,仅需要将指针数组传递给函数即可,五位字母来区分,字母为“v”(vetor)的表示:指针数组方式
[3]向新程序传递环境表的方式 (注意这些参数必须以NULL结尾。)
继承调用进程的environ变量:其他函数
指定新的环境字符串指针数组envp[]:以“e”(Enviromen)结尾的两个函数execle、execve
返回值:
如果exec执行成功是不会返回的,除非失败返回-1。

环境表
每个程序都接收到一张环境表。与参数表一样,环境表也是一个字符指针数组,其中每个
指针包含一个以NULL结束的字符串的地址。全局变量environ则包含了该指针数组的地址。

int main(int argc, char *argv[], char *envp[])

常见的错误原因有:
1)找不到文件或路径,此时errno被设置为ENOENT
2)数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT
3)没有对应可执行文件的运行权限,此时errno被设置为EACCESS

【3】exit 和 _exit 终止进程
exit()和_exit()函数都是用来终止进程的。当程序执行exit()和_exit()时,进程会无条件的停止剩下的所有操作。

#include <unistd.h>
void _exit(int status);

#include <stdlib.h>
void exit(int status);
函数功能:
终止进程
函数参数:
[1] status :表示进程结束时的状态将 status & 0377(低8位有效)返回给父进程,父进程可以使用wait()函数回收这个值。
EXIT_SUCCESS 正常结束
EXIT_FAILURE 异常结束
习惯 上 0 代表成功 !0 代表异常终止

注意:这两个函数的区别
_exit()函数的作用最为简单:直接使进程终止运行
exit()则在这些基础上作了一些处理: 执行 “退出处理函数” 和 “清除I/O缓存”

退出处理函数
#include <stdlib.h>
int atexit(void (*function)(void));
函数功能:
注册进程的退出处理函数(进程正常终止 exit)
函数参数:
[1] function : 函数指针,用于指定退出处理函数
函数返回值:
成功 返回 0
失败 返回 !0
注意:

  1. 一个函数可以注册多次,注册多次就执行几次
  2. 如果注册多个退出处理函数,退出处理函数执行的顺序 和 注册 顺序相反。

【4】wait & waitpid 回收子进程的退出状态

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
函数功能:
阻塞等待子进程结束(任意子进程),并回收子进程的退出信息
函数参数:
[1] status :
!NULL 用于保存子进程的退出状态信息
NULL 只回收子进程不保存退出信息
返回值:
成功 返回 子进程的PID
失败 返回 -1

注意:
status 不仅仅包含了子进程的退出状态(exit 指定的退出状态),还包含其他退出信息

WIFEXITED(status) 根据子进程的退出信息,判断子进程是否正常结束,如果是返回真,否则返回假
WEXITSTATUS(status) 根据子进程的退出信息,获取子进程的退出状态
WIFSIGNALED(status) 根据子进程的退出信息,判断子进程是否被信号终止,如果是返回真,否则返回假

pid_t waitpid(pid_t pid, int *status, int options);
函数功能:
可以指定等待某个子进程结束以及等待的方式(阻塞或非阻塞)。事实上wait()函数只是waitpid()函数的特例
函数参数:
[1] pid :
= -1 表示等待任意子进程
> 0 表示子进程的进程号(pid)
= 0 表示等待同一进程组内的任意子进程
< -1 表示等待进程组ID = |pid|的进程组内的任意子进程
[2] status : 用法同wait函数
!NULL 用于保存子进程的退出状态信息
NULL 只回收子进程不保存退出信息
[3] options : 选择等待方式
阻塞 0
非阻塞 WNOHANG
返回值:
成功 返回 子进程的PID
返回 0 (options = WNOHANG 并没有回收任何子进程)
失败 返回 -1

wait(&status) == waitpid(-1, &status, 0);

怎出处理僵尸进程:
1)真父wait
父进程调用wait或waitpid回收子进程
2)父进程提前退出
子进程成为"孤儿进程",init进程会成为子进程的"继父",回收僵尸进程
3)真父委托继父
当子进程结束时,内核会向父进程发送SIGCHLD信号
如果父进程忽略了SIGCHLD信号,表示父进程不关心子进程的退出状态。init会代替父进程回收僵尸进程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值