Linux 多进程(一)

Linux 多进程

进程

进程理论相关内容直接看教材就好现代操作系统等

shell运行程序的过程:
用户键入命令->shell建立一个新进程来运行此程序->shell将程序从磁盘载入->程序在它的进程中运行直到结束

进程=程序+数据集合

进程是操作系统动态执行的基本单元,基本的分配单元

Linux 多进程(一)_第1张图片

Linux 多进程(一)_第2张图片

进程的状态和状态转换

进程的三个基本状态:就绪态,运行态,阻塞态

五种状态的模型:创建态,就绪态,运行态,阻塞态,终止态

Linux 多进程(一)_第3张图片

Linux 多进程(一)_第4张图片

Linux 多进程(一)_第5张图片

Unix/Linux进程命令

ps命令:

ps aux / ajx
a:显示终端上的所有进程,包括其他用户的进程
u:显示进程的详细信息
x:显示没有控制终端的进程
j:列出与作业控制相关的信息

Linux 多进程(一)_第6张图片

Linux 多进程(一)_第7张图片

当前运行的命令也是一个进程

ps -ajx

Linux 多进程(一)_第8张图片

PPID:父进程ID

PID:进程IDPGID:进程组的ID(一个组中可以有多个进程)

SID:会话ID:一个会话包含一个或多个组

会话是由会话中的第一个进程创建的,一般是打开终端时创建的shell进程(领头进程),会话中领头进程的PID即为会话的SID

STAT参数意义:
D	不可中断Uninterruptible (usually Io)
R	正在运行,或在队列中的进程
s(大写)	I处于休眠状态
T	停止或被追踪
Z	僵尸进程
w	进入内存交换(从内核2.6开始无效)
x	死掉的进程
< 	高优先级
N	低优先级
s	包含子进程
+	位于前台的进程组

top命令

实时显示进程的动态

-d参数指定信息更新的时间间隔

top
可以在使用top命令时加上 -d 来指定显示信息更新的时间间隔,在top命令执行后.可以按以下按键对显示的结果进行排序:
M	根据内存使用量排序
P	根据cPU占有率排序
T	根据进程运行时间长短排序
u	根据用户名来筛选进程
K	输入指定的 PID杀死进程

Linux 多进程(一)_第9张图片

kill命令

杀死进程

kill -l,显示所有的kill信号

kill [-signal] pid
kill -l 列出所有信号
kill -SIGKILL 进程ID
kill -9 进程ID
killall name 根据进程名字杀死进程

让程序在后台运行

例如:

#./可执行程序名+&
./hello &

PID,PPID,PGID和相关的函数

每个进程都由进程号来标识:

范围:0-32767(可以修改)

进程号对于一个进程来说是唯一的,但是可以在一个进程终止后,重用其进程号

类型:pid_t(整型)

Linux 多进程(一)_第10张图片

Linux 多进程(一)_第11张图片

fork

#include 

       pid_t fork(void);

每次调用返回两次,父进程中返回的是子进程的PID,子进程中返回0

新进程拥有和父进程相同的代码和数据(运行到相同的地方)(从fork返回的地方开始)

(父子进程的代码完全相同)

如果返回-1,失败:

失败的普遍原因:
(1)当前系统进程数达到系统规定上限:errno被设置为EAGAIN

(2)系统内存不足:errno设置为ENOMEM

此返回值是条件判断父子进程的依据:

#include
#include
#include

int main()
{
    int res_form_fork,mypid;
    mypid = getpid();
    printf("before id:%d\n",mypid);
    res_form_fork = fork();
    sleep(1);
    //打印创建子进程前后的pid和fork返回值
    print("after pid is:%d fork():%d\n",getpid(),res_form_fork);
    return 0;
}


/* 结果:

root@ziggy-virtual-machine:~/unix_linux/chapter8# gcc -o forkdemo forkdemo.c 
root@ziggy-virtual-machine:~/unix_linux/chapter8# ./forkdemo 
before id:1291
after pid is:1291 fork():1292
after pid is:1292 fork():0

*/

以下代码会有八行输出,也就是总共7个进程

//分别是父进程,子1,子2,子3,子1.1,子1.1.1,子2.2
#include
#include
#include

int main()
{
    printf("pid is:%d\n",getpid());
    fork();
    fork();
    fork();
    //打印创建子进程前后的pid和fork返回值
    printf("pid is:%d\n",getpid());
    return 0;
}

区分父子进程:根据返回值判断

#include
#include
#include

int main()
{
    printf("before pid is:%d\n",getpid());
    int res = fork();
    if(res==-1)
    {
        printf("error\n");
    }
    else if(res==0)
    {
        printf("child pid is:%d\n",getpid());
    
    }
    else
    {
        printf("parent pid is:%d\n",getpid());
    
    }
    return 0;
}

/*
root@ziggy-virtual-machine:~/unix_linux/chapter8# ./forkdemo.3 before pid is:1680
child pid is:1680
parent pid is:1681
*/

子进程的PPID被设置为父进程的PID

原进程设置的信号处理函数对新进程不再起作用

数据的复制是写时复制,只有任一进程对数执行了写操作,复制才会发生

父进程中打开的文件描述符在子进程中默认打开,且引用计数+1,且父进程的用户根目录,当前工作目录,的引用计数都+1

可以看Linux高性能服务器编程第13章第一部分

#include
#include
// #include0){//parent
        printf("pid:%d\n",pid);//子进程进程号
        printf("i am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
    }else if(pid==0){
         printf("i am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    for(int i = 0;i<5;i++)
    {
        printf("i:%d\n",i);
    }

    return 0;
}

Linux 多进程(一)_第12张图片

再看一看:ps aux进程的信息

Linux 多进程(一)_第13张图片

上面表示,终端是一个进程,而程序是终端的子进程

父子进程是交替运行的(CPU分配时间片)

在没有添加for循环的时候,子进程getppid()得到的返回值为1。

原因是此时父进程被杀死,子进程被init进程领养,可以在for循环中将子进程的存活时间设置得比父进程长,分别在父进程被杀死前后查看ppid,可以观察到这一变化。

fork的返回值pid为局部变量在栈空间

父进程存储在栈空间的返回值pid为子进程id,子进程返回的pid为0

父子进程相同的地方:

内核区复制一份,但是pid不同,用户区数据相同

测试局部变量:测试父子进程都拥有number后,互相之间是否影响(子进程拷贝一份局部变量)

#include
#include

int main()
{

    int num = 10;
    pid_t pid = fork(); 
    if(pid>0)
    {
        printf("parent num:%d\n",num);
        printf("parent num address:%p\n",&num);
    }else if(pid==0){
        num+=10;
        printf("child num:%d\n",num);
        printf("child num address:%p\n",&num);

    }
    return 0;

}

Linux 多进程(一)_第14张图片

两个num互不影响,虚拟地址空间相同,但是映射在物理空间地址就不一样了

linux系统下每个进程都拥有自己的页表,父进程fork出新的子进程时,子进程拷贝一份父进程的页表,且父子进程将页表状态修改为写保护。当父进程或子进程发生写操作时将会发生缺页异常,缺页异常处理函数将会为子进程分配新的物理地址。

栈空间数据读时父子进程共享一块物理内存,写时复制,开辟一块新的内存

你可能感兴趣的:(linux,java,python,操作系统,shell)