进程相关。

  1. 进程的概念

程序的每一次执行都会产生一个进程。

进程随着程序的运行而开始,随着程序的结束而结束。

每个进程都会分配0~3G的用户空间(内存)。3~4G的内核空间(内存)。进程是分配资源的最小单位。

用户空间可以有多份,内核空间只有一份。

进程和进程之间是相互独立的,比如每个进程都会有自己的文件描述符,都会有自己的缓冲区。

进程在内核中是一个task_struct结构体,在这个结构体中包含了进程相关的所有信息。

在系统上多个进程同时运行:时间片轮询,上下文切换。

2.进程和程序的区别

程序的每次执行会产生一个进程,进程的本质是存储在内存上的。

进程是有生命周期的,随着程序的启动而启动,随着程序的终止而终止,是动态变化的(进程终止不代表进程死亡)。是动态变化的。

程序是编译生成的二进制文件,存在硬盘上。

程序是静态的,没有生命周期。

3.进程的组成

task_struct 结构体,数据段,文本段。

4.进程的种类

交互进程:交互进程通常维护一个终端,我们用户可以通过终端和这个进程进行交互。

批处理进程:批处理进程是放在一个队列中,等待cpu运行。批处理进程的优先级较低。

gcc 编译程序就是批处理进程。

守护进程:

守护进程随着系统的启动而启动,随着系统的停止而停止。

比如我们windows上的各种服务就是守护进程。

5.进程的PID

在linux中每个进程都有一个PID,这个PID是进程在系统中的唯一标识。

系统中进程的最大数量:

特殊进程的pid:

0号进程:又叫做空闲进程,当系统上没有任何程序运行时,就运行0号进程。0号进程是用来启动1号进程和2号进程。启动完成后,0号进程结束。

1号进程:1号进程又叫做init进程。init进程的作用就是对系统进行各种初始化。初始化完成之后,init进程会一直存在。init进程会回收孤儿进程的资源。

2号进程:(kthreadd)进程,是调度进程。负责系统上各个进程的调度工作。

6.进程相关的命令

cpp 复制代码
pidof a.out 查看有a.out运行产生的进程的pid
ps 查看进程的相关信息
ps -ef ------> 查看进程的pid,ppid
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 10月10 ?      00:00:49 /sbin/init auto nopro
root           2       0  0 10月10 ?      00:00:00 [kthreadd]
root           3       2  0 10月10 ?      00:00:00 [rcu_gp]
root           4       2  0 10月10 ?      00:00:00 [rcu_par_gp]

UID:用户ID
PID:进程的PID
PPID:进程的父进程的PID
C:CPU占用率
STIME:进程的启动时间
TTY:是否和终端相关联
TIME:占用CPU的时间
CMD:启动进程的命令

ps -ajx  //用来查看进程的状态
linux@kong:/proc$ ps -ajx
   PPID     PID    PGID     SID TTY        TPGID STAT   UID   TIME COMMAND
      0       1       1       1 ?             -1 Ss       0   0:02 /sbin/init auto noprompt
      0       2       0       0 ?             -1 S        0   0:00 [kthreadd]
      2       3       0       0 ?             -1 I<       0   0:00 [rcu_gp]
PID:进程的PID
PPID:进程的父进程的PID
PGID:进程组ID,在终端上每次./a.out运行就会长生一个进程组
    进程组分为前台进程组和后台进程组。前台进程组只能有一个
SID:会话ID,一个终端就是一个会话。
TTY:是否和终端相关联
TPGID:终端前台进程组ID
STAT:进程的UID
UID:进程的UID
TIME:占用的CPU的时间


sudo apt-get install htop
top/htop:动态查看进程的相关信息
进程号 USER      PR  NI    VIRT    RES    SHR    %CPU  %MEM     TIME+ COMMAND     
  47772 linux     20   0    2364    508    444 R  99.0   0.0   2:30.98 a.out       
  47173 linux     20   0    2364    576    512 R  89.5   0.0 104:45.86 a.out       
PR:进程的优先级
NI:nice值 --- nice可以影响进程的优先级
nice:[-20~19] nice值越小,优先级越高

ps -eo pid,ni,cmd|grep a.out 查看a.out进程的pid,ni,cmd
sudo nice -n nice值(-20~19)./a.out
sudo renice nice值  进程的PID

killall a.ot 杀死所有的同名进程
kill -9 PID 杀死PID对应的进程
kill -19 PID 停止进程
kill -18 PID 继续运行进程

linux@kong:/proc$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

7.进程的状态

cpp 复制代码
D    不可中断的休眠态(不可以被信号中断)
R    运行态
S    休眠态
T    停止态
t    被调试器停止的状态(比如使用GDB调试程序,停止的状态)
X    死亡态,无法看到
Z    僵尸态

<    高优先级
N    低优先级
L    在内存区锁定
s    是一个会话的组长
l    多线程
+    前台进程

8.进程的状态切换实例

  • 产生一个前台进程
  • 使用ctrl + z使前台进程变成了 停止态的进程
  • 使用kill -18 可以使停止态的进程变成运行态的进程(后台运行)
  • 使用jobs -l(小写的L) 命令可以查看进程的作业号
  • fg + 作业号 使后台进程变成前台进程
  • bg + 作业号 使进程(停止态) 后台运行

9.进程创建原理

进程的创建是通过拷贝其父进程的资源完成的,子进程的所有资源都来自于他的父进程。

子进程创建之后,和父进程相互独立,互不影响。

10.进程创建的函数

cpp 复制代码
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:创建一个子进程
参数:void
返回值:
    成功:父进程返回子进程的PID,子进程返回0
    失败:返回-1,置位错误码

11.进程创建的实例

11.1 进程创建的实例1(不关注返回值)

cpp 复制代码
#include <my_head.h>

int main(int argc, const char *argv[])
{
	fork();
	while(1);
	return 0;
}

11.2 fork 函数的使用实例2(不关注返回值)

cpp 复制代码
#include <my_head.h>

int main(int argc, const char *argv[])
{
	fork();//第一次fork调用完成,父进程产生一个子进程

	fork();//第二次调用完成,父进程再产生一个子进程,
		   //第一次的子进程也产生一个子进程,目前共4个进程

	while(1);

	return 0;
}

11.3 fork()使用实例3(不关注返回值)

cpp 复制代码
#include <my_head.h>

int main(int argc, const char *argv[])
{
	printf("#");
	fork();//第一次fork调用完成,产生了两个进程,每个进程的缓冲区
    //中,都有一个# 

	fork();//第二次fork,两个进程变成了四个进程,
    //四个进程中每个进程的缓冲区都有一个#号
	//终端输出多少个#号-----4个

	return 0;
}

11.4 使用实例(不关注返回值)

cpp 复制代码
#include <my_head.h>

int main(int argc, const char *argv[])
{
	for(int i = 0; i < 2; i++){
	printf("#");
	fork();
	}
	//输出多少#?
	return 0;
}

fork n次会产生2的n次方个子进程

11.5 fork使用实例:关注返回值

getip()函数返回当前进程的pid

getppid()函数返回当前进程的父进程的pid

cpp 复制代码
#include <my_head.h>

int main(int argc, const char *argv[])
{
	pid_t ret;
	ret = fork();
	if(-1 == ret){
	PRINT_ERR("fork error");
	}else if(0 == ret){
		while(1){
			printf("我是子进程,pid = %d,ppid = %d\n",\
					getpid(),getppid());
			sleep(1);
		}
	}else{
		while(1){
			printf("我是父进程,pid = %d,子pid = %d\n",\
					getpid(),ret);
			sleep(1);
		}
	}
	
	return 0;
}

12.父子执行先后顺序问题

没有先后顺序,时间片轮询,上下文切换

13.父子进程内存空间问题

cpp 复制代码
#include<my_head.h>

int main(int argc,const char *argv[]){
    pid_t ret;
    int a = 100;
    ret = fork();
    //子进程中 ret == 0
    //父进程中 ret == 子进程的pid
    if(-1 == ret){
        PRINT_ERR("fork error");
    }else if(0 == ret){
        //子进程
        while(1){
            printf("我是子进程,a=%d\n",a);
            a = 200;
            printf("a修改后的数值是%d\n",a);
            sleep(1);
        }
    }else{
        //父进程
        while(1){
            printf("我是父进程,a=%d\n",a);
            sleep(1);
        }
    }
    return 0;
}

进程创建中的 写时 拷贝问题

copy-on-write

子进程在创建的时候,是拷贝父进程的资源产生的。

但是如果子进程不对拷贝过来的数据进行修改,那么本质上父子进程的数据段和代码段用的使同一块内存空间。

只有当子进程尝试对拷贝过来的数据进行修改时,才会真正的拷贝出一份数据的副本。

15.使用fork函数创建出三个进程。

cpp 复制代码
#include <my_head.h>

int main(int argc, const char *argv[])
{
    pid_t ret;
    int a = 100;
    ret = fork();
    if (-1 == ret)
    {
        PRINT_ERR("fork error");
    }
    else if (0 == ret)
    {
        pid_t ret1;
        ret1 = fork();
        if (-1 == ret1)
        {
            PRINT_ERR("fork error");
        }
        else if (0 == ret1)
        {
            while (1)
            {
                printf("我是进程A,ipid = %d\n", getpid());
                sleep(1);
            }
        }
        else
        {
            while (1)
            {
                printf("我是进程B,pid = %d\n", getpid());
                sleep(1);
            }
        }
    }
    else
    {
        while (1)
        {
            printf("我使进程C,pid = %d\n", getpid());
            sleep(1);
        }
    }
    return 0;
}
  1. 孤儿进程

父进程结束,子进程没结束,此时子进程就是孤儿进程。

孤儿进程会被init进程收养,由init进程为子进程回收资源。

注意:孤儿进程不是进程状态。

cpp 复制代码
#include<my_head.h>

int main(int argc,const char *argv[]){
    pid_t ret;
    ret = fork();
    //子进程中 ret == 0
    //父进程中 ret == 子进程的pid
    if(-1 == ret){
        PRINT_ERR("fork error");
    }else if(0 == ret){
        //子进程
        while(1){
            printf("我是子进程,我还活着\n");
            //此时子进程就变成了孤儿进程
            sleep(1);
        }
    }else{
            printf("我是父进程,我死了\n");

    }
    return 0;
}

17.僵尸进程

子进程结束后,父进程没结束,且父进程没有为子进程回收资源(调用wait函数),此时子进程就是僵尸进程。

僵尸进程会照成系统资源的浪费。

cpp 复制代码
#include<my_head.h>

int main(int argc,const char *argv[]){
    pid_t ret;
    ret = fork();
    //子进程中 ret == 0
    //父进程中 ret == 子进程的pid
    if(-1 == ret){
        PRINT_ERR("fork error");
    }else if(0 == ret){
        //子进程
        printf("我是子进程,我死了\n");
    }else{
        while(1){
            printf("我是父进程,我还活着\n");
            sleep(1);
        }
    }
    return 0;
}

18.进程退出的函数 exit() _exit()

只有在main函数中调用return才会结束进程。

cpp 复制代码
#include <unistd.h>
void _exit(int status);
功能:退出进程
参数:
    status:进程退出的状态  (一般填写0~255的数字)
返回值:空

#include <stdlib.h>
void exit(int status);
功能:退出进程
参数:
    status:进程退出的状态  (一般填写0~255的数字)
返回值:空

exit 退出会刷新缓冲区
_exit 退出不会刷新缓冲区

EXIT_SUCCESS:成功
EXIT_FAILURE:失败

exit代码示例

cpp 复制代码
#include<my_head.h>

int test_fucn(){
    //return 0;//退出函数,进程不会结束,下面的1111会输出
    exit(0);//进程直接结束,1111111111不会输出
}

int main(int argc,const char *argv[]){
    //test_fucn();
    printf("111111111111");
    //exit(0);//库函数,会刷新缓冲区,11111111会输出
    _exit(EXIT_SUCCESS);//系统调用,不会刷新缓冲区,缓冲区中的数据丢失
    
    return 0;
}
相关推荐
2202_754421543 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
努力的悟空30 分钟前
国土变更调查拓扑错误自动化修复工具的研究
运维·自动化
运维&陈同学1 小时前
【zookeeper03】消息队列与微服务之zookeeper集群部署
linux·微服务·zookeeper·云原生·消息队列·云计算·java-zookeeper
旦沐已成舟1 小时前
DevOps-Jenkins-新手入门级
服务器
周末不下雨2 小时前
win11+ubuntu22.04双系统 | 联想 24 y7000p | ubuntu 22.04 | 把ubuntu系统装到1T的移动固态硬盘上!!!
linux·运维·ubuntu
软件技术员2 小时前
Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书
服务器·网络协议·ssl
哎呦喂-ll2 小时前
Linux进阶:环境变量
linux
耗同学一米八2 小时前
2024 年河北省职业院校技能大赛网络建设与运维赛项样题四
运维·网络
Rverdoser2 小时前
Linux环境开启MongoDB的安全认证
linux·安全·mongodb