进程

进程

进程

进程的含义

进程是一个程序执行的过程(也可以说是正在运行的程序),会去分配内存资源,cpu的调度,它是并发的

PCB块

PCB是一个结构体,全名是process control block/print circuit board;

一个程序运行就有一块pcb块,它记录着运行程序的所有信息和状态
pcb里包含着:

PID---进程标识符

chdir---当前工作路径

umask 0002

fds---进程打开的文件列表

signal---信号相关设置 处理异步io:

用户id,组id:

进程资源的上限(ulimit -a,显示资源上限);

进程和程序的区别

程序是静态

存储在硬盘中代码,数据的集合;

进程是动态

程序执行的过程,包括进程的创建、调度、消亡;

(1)程序是永存,进程是暂时的

(2)进程有程序状态的变化,程序没有

(3)进程可以并发,程序无并发

(4)进程与进程会存在竞争计算机的资源

(5)一个程序可以运行多次,变成多个进程;一个进程可以运行一个或多个程序

内存空间

32位内存空间分布图:

虚拟地址空间共4G:0~ 3G,是进程的空间,3G~4G是内核的空间

虚拟地址 :

映射表将虚拟内存的地址转换为物理内存的地址;

映射表通常由页表(Page Table)组成,它记录了虚拟地址到物理地址的映射关系;

一个页面大小通常为4k(4036字节);

进程分类:

1、交互式进程

2、批处理进程 (shell脚本)

3、 守护进程

进程的作用

并发性:允许多个进程同时运行,提高了CPU利用率和系统响应速度;

稳定性:如果一个进程发生错误,通常不会影响到其他进程,这提高了系统的稳定性;

进程的状态进程已经准备好执行,所有的资源都已分配,只等待CPU时间

基本操作系统三个操作状态:就绪→执行态→阻塞(等待,睡眠);
就绪状态 :进程已经准备好执行,所有的资源都已分配,只等待CPU时间

执行

linux中的状态:运行态,睡眠态,僵尸,暂停态;

进程的调度

调度器的通过调度策略来决定哪个进程先运行;
进程上下文切换 :就是值指调度器要切换CPU给另一个进程的时候,要保存当前进程的状态,然后加载打开一个新的进程这样的一个过程。

宏观并行:在一个时间段多个任务和进程是同时进行的

微观串行:在一个时间点只能一条一条指令执行

进程相关命6.查询进程相关命令

1.ps aux

查看进程相关信息:

就绪态、运行态 ------ R

睡眠态、等待态

可唤醒等待态 ------S

不可唤醒等待态 ------D

停止态 ------T

僵尸态 ------Z

2.top

根据CPU占用率查看进程相关信息

PR NI 表示优先级

数字越小代表优先级越高

3.kill和killall发送一个信号

kill -2 PID 15

发送信号+PID对应的进程,默认接收者关闭

killall -9 进程名

发送信号 进程名对应的所有进程

killall a.out

函数

1.fork();

函数原型

c 复制代码
pid_t fork();

一次调用,会返回两次。

子进程先运行和是父进程先进程,顺序不确定。

变量不共享。

子进程复制父进程的0到3g空间和父进程内核中的PCB,但id号不同。
功能

通过该函数可以从当前进程中克隆一个同名新进程。

克隆的进程称为子进程,原有的进程称为 父进程。

子进程是父进程的完全拷贝。

子进程的执行过程是从fork函数之后执行。

子进程与父进程具有相同的代码逻辑。
返回值

int 类型的数字。

在父进程中:成功 返回值是子进程的pid号 >0

失败 返回-1;

在子进程中:成功 返回值 0

失败 无

举例问题:

一次fork生成几个进程?他们之间的关系是什么样的?

一次fork生成两个进程,父子关系。

如果两次fork同时前后执行,会生成几个进程?他们之间的关系如何表示,有多少个子进程,有没有孙进程?

会生成三个进程,父进程,子进程和孙进程。

2.getpid

函数原型

c 复制代码
pid_t getpid(void);

功能 :

获得调用该函数进程的pid
参数 :

缺省
返回值 :

进程的pid

3.getppid

函数原型

c 复制代码
pid_t getppid(void);

功能 :

获得调用该函数进程的父进程pid号
参数 :

缺省
返回值 :

返回父进程id号

示例代码

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
int a = 20;
int main(int argc, char *argv[])
{
    pid_t ret = fork();
    if(ret>0)
    {
        //father
            sleep(3);
            printf("father is %d   pid %d ,ppid:%d  \n",a,getpid(),getppid());
    }
    else if(0 == ret)
    {
        //child
            printf("child a is %d\n",a);
            a+=10;
            printf("child a is %d pid:%d ppid:%d\n",a,getpid(),getppid());
    }
    else 
    {
        perror("fork error\n");
        return 1;
    }
    printf("a is %d pid:%d\n",a,getpid());
    return 0;
}

应用场合:

1)一个进程希望复制自己,使父子进程同时执行同的代码段。网络服务中会比较多见。

2)一个进程需要执行一个不同的程序。fork+exec

父子进程关系

子进程是父进程的副本,子进程获得父进程数据段,堆、栈正文段共享;

区别:

fork的返回值 父的是大于0,子是等于0;

pid不同 子相对于父至少要加个1;

进程的终止

8种情况

1)main 中return

2)exit(), c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。已经清理函数(atexit)。

3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。

  1. 主线程退出

5)主线程调用pthread_exit 异常终止

6)abort() 越界报错

7)signal kill pid

8)最后一个线程被pthread_cancle

进程的退出

僵尸进程和孤儿进程

僵尸进程:进程执行结束但空间未被回收变成僵尸进程(pcb不释放会导致内存越来越小导致内存崩溃);

孤儿进程:父进程先消亡

1.exit

属于库函数

退出状态,终止的进程会通知父进程,自己使如何终止的。如果是正常结束(终止),则由exit传入的参数。如果是异常终止,则有内核通知异常终止原因的状态。任何情况下,负进程都能使用wait,waitpid获得这个状态,以及资源的回收。

函数原型

c 复制代码
void exit(int status)
调用: 
exit(1);

功能 :

让进程退出,并刷新缓存区
参数
status :进程退出的状态
返回值 :

缺省

EXIT_SUCCESS 0

EXIT_FAILURE 1
相当于return

但当该关键字出现在main函数中时候可以结束进程

如果在其他函数中则表示结束该函函数;
示例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{    
    FILE* fp = fopen("1.txt","w");
    char buf[512]="hello,123";
    fputs(buf,fp);
    exit(0);
    printf("aaaaaaaaaaa\n");
    return 0;
}

2._exit

属于系统调用

函数原型

c 复制代码
void _exit(int status);

功能 :

让进程退出,不刷新缓存区
参数 :

status:进程退出状态
返回值 :

缺省;
示例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    FILE* fp = fopen("1.txt","w");
    char buf[512]="hello,123";
    fputs(buf,fp);
    _exit(0);// 不会刷新缓冲区  
    printf("aaaaaaaaaaa\n");
    return 0;
}

3.atexit

回调函数
函数原型

c 复制代码
int atexit(void (*function)(void));

功能 :

注册进程退出前执行的函数
参数 :

function:函数指针

指向void返回值void参数的函数指针
返回值 :

成功返回0

失败返回非0

当程序调用exit或者由main函数执行return时,所有用atexit

注册的退出函数,将会由注册时顺序倒序被调用
示例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
char * p ; 
void clean(void)
{
    printf("clean func,p %s\n",p);
    free(p);
}
int main(int argc, char *argv[])
{
    atexit(clean);
    p = (char*)malloc(50);
    strcpy(p,"hello");
    printf("main p %s\n",p);
    exit(0);
    return 0;
}

进程空间的回收

wait/waitpid

wait

函数原型

c 复制代码
pid_t wait(int *status);

功能

该函数可以阻塞等待任意子进程退出并回收该进程的状态。

一般用于父进程回收子进程状态。

参数

status 进程退出时候的状态:

如果不关心其退出状态一般用NULL表示;

如果要回收进程退出状态,则用WEXITSTATUS回收。

返回值

成功 回收的子进程pid

失败 -1;

WIFEXITED(status) 是不是正常结束

WEXITSTATUS(status) 使用这个宏去那返回值

WIFSIGNALED(status) 是不是收到了信号而终止的

WTERMSIG(status)如果是信号终止的,那么是几号信号。
注意wait

1)如果所有的子进程都在运行,在阻塞

2)如果一个子进程终止,正在等待的父进程则获得终止状态,获得子进程的状态后,立刻返回。

3)如果没有子进程,则立即出错退出。

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

waitpid

函数原型

c 复制代码
pid_t waitpid(pid_t pid, int *status, int options);

pid

小于-1 回收指定进程组内的任意子进程;

等于-1 回收任意子进程,组内外;

等于0 回收和当前调用waitpid一个组的所有子进程,组内;

大于0 回收指定ID的子进程;

waitpid (-1,a,0) == wait(a);

status

子进程退出时候的状态,

如果不关注退出状态用NULL;

options 选项

0 表示回收过程会阻塞等待

WNOHANG 表示非阻塞模式回收资源。

返回值:成功 返回接收资源的子进程pid

失败 -1

0,

exec

execute
exec族

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建; 新进程,所以调用exec前后该进程的id并未改变。

其实有六种以exec开头的函数,统称exec函数:
函数原型

c 复制代码
#include <unistd.h>
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 execle(const char *path, const char *arg, ..., char *const envp[]);

int execve(const char*path,char*const argv[],char*const evnp[]);
int execlp(const char *file, const char *arg, ...);

int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

区别

1)前4个使用路径名作为参数,后面两个使用文件名做参数

当filename中,含有/时视为路径名,否则就按PATH变量,在指定目录下查找可执行文件。

2)相关的参数表传递,l表示list,v表示vector;

execl,execlp,execle,需要将参数一个一个列出,并以NULL结尾。

execv,execvp,execve,需要构造一个参数指针数组,然后将数组的地址传入。

3)以e结尾的函数,可以传入一个指向环境字符串的指针数组的指针。其他未指定环境变量,使用父进程继承过来的。

execve 是真正的系统调用

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错;则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

system

函数原型

c 复制代码
int system(const char *command); fork+exec 

功能

使用该函数可以将shell命令直接在代码中执行。
参数

command要执行的shell命令
返回值

成功 0

失败 -1

相关推荐
小狮子安度因13 分钟前
边缘智能-大模型架构初探
linux·网络
晨春计16 分钟前
【git】
android·linux·git
Flying_Fish_roe28 分钟前
linux-软件包管理-包管理工具(RedHat/CentOS 系)
linux·运维·centos
Splashtop高性能远程控制软件31 分钟前
centos远程桌面连接windows
linux·windows·centos·远程控制·远程桌面
tang&1 小时前
【Linux】进程概念
linux
苏少朋1 小时前
Docker安装 ▎Docker详细讲解 ▎数据卷挂载 ▎Nginx安装理解
linux·nginx·docker·容器
Jerry 二河小鱼1 小时前
在Linux中安装FFmpeg
linux·运维·服务器·ffmpeg
Roc-xb2 小时前
如何在 CentOS 上安装和使用 Neofetch(图文教程)
linux·运维·centos
程序员大阳2 小时前
Linux(6)--CentOS目录
linux·centos·目录
小技与小术2 小时前
lvs命令介绍
linux·运维·服务器·lvs