Linux 学习记录24(进程线程篇)

Linux 学习记录24(进程线程篇)

在这里插入图片描述

一、进程

1. 特殊进程(PID: 0,1,2)

  1. 0进程(idel进程):是linux系统启动后的第一个进程,这个进程也叫空闲进程当没有其他进程执行时回执行该进程
  2. 1进程(init进程):他是在0号进程产生,这个进程主要用于硬件的初始化工作,也是其他进程的父进程,可以完成对一些进程的收尸工作
  3. 2进程(kthreadd进程):也称为调度进程,这个进程也是由0号进程产生,他的任务是完成任务调度工作

2. 进程相关命令

(1. ps指令

  1. ps -ef:可以显示进程间的关系

UID:用户ID
PID:当前进程的ID
PPID:当前进程的父进程的ID
C:目前不用关注
TIME:运行日期
TTY:如果显示问号,则说明没有终端与该进程对应
CMD:进程名
在这里插入图片描述

  1. ps -ajx:查看进程的运行状态

PGID: 组id号
SID:会话ID号
TPGID:如果值为-1说明是守护进程(服务进程)
STAT:进程的状态
在这里插入图片描述

  1. ps -aux:显示进程在计算机资源的占比

在这里插入图片描述

(2. kill指令

  1. kill -l:查看系统提供的信号有那些

ctrl+c 等同 2) SIGINT
ctrl+z 等同 19) SIGSTOP
9) SIGKILL 杀死一个进程
18) SIGCONT 继续一个进程
前30个是稳定信号,后面的是不稳定信号
在这里插入图片描述

  1. killall 进程名:杀死所有的该进行名对应的进程>>

程序
在这里插入图片描述
终端
在这里插入图片描述
杀死
在这里插入图片描述

  1. kill -信号号 pid:给进程发送信号

(3. 查看进程号

pidof 进程名:给进程发送信号
在这里插入图片描述

(4. 动态显示进程状态

top
在这里插入图片描述

(5. 进程树

pstree
在这里插入图片描述

二、进程的状态

1. 进程状态描述

进程普通状态

D uninterruptible sleep (usually IO) 不可中断
R running or runnable (on run queue) 运行/可运行状态
S interruptible sleep (waiting for an event to complete) 可终断的睡眠态
T stopped by job control signal 停止态
t stopped by debugger during the tracing 调试停止态
W paging (not valid since the 2.6.xx kernel) 弃用
X dead (should never be seen) 死亡态,不能被看到
Z defunct (“zombie”) process, terminated but not reaped by its parent 僵尸态,子进程结束,父进程没有为其收尸

进程附加状态

  1. < high-priority (not nice to other users) 高优先级进程
  2. N low-priority (nice to other users) 低优先级进程
  3. L has pages locked into memory (for real-time and custom IO) 加锁的进程,不可用放入swap分区内
  4. s is a session leader 会话组组长进程
  5. l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) 多线程的进程
  6. + is in the foreground process group 前台进程

2. 进程切换

在这里插入图片描述

3. 进程状态切换的实例

在程序运行时查看经常状态
程序
在这里插入图片描述
状态
在这里插入图片描述
ctrl + z 进入停止态
在这里插入图片描述
恢复运行态 kill -18 PID 恢复到运行态并后台运行
在这里插入图片描述
恢复运行态 kill -19 PID 改为停止态查看作业号
查看作业号bg 有&说明是后台运行
在这里插入图片描述
在这里插入图片描述
将后台运行变为前台运行
作业号fg 作业号 以将后台运行的进程切换成前台运行,如果后台只有一个进程,则直接执行fg即可

在这里插入图片描述

(1. 休眠进程的切换

休眠状态
在这里插入图片描述
运行时的状态(为前台休眠状态)
在这里插入图片描述
切换为停止态
在这里插入图片描述
在这里插入图片描述
+bg 将停止态的进程变为后台休眠态
在这里插入图片描述
在这里插入图片描述
+fg将后台休眠态改为前台休眠态
在这里插入图片描述
在这里插入图片描述

三、进程的创建

进程创建过程,是通过拷贝父进程得到的,进程的内核空间中是通过task_struct结构体表述的,新经常在创建过程中,直接拷贝父进程的该结构体得到,只需要稍微修改即可,保留了父进程的的大部分遗传信息

1. 进程创建函数fork

注意事项:

  1. 子进程和父进程在不同的内存空间在运行,同时其中一个进程的运行不会影响另外一个进程的运行
  2. 父进程会拷贝一份资源给子进程,他们的资源是一致的,但是子进程不会执行fork之前的语句
  3. 父子进程先执行哪一个是不确定的,由CPU的调度机制决定
  4. 子进程会拷贝父进程的虚拟空间,父子进程的0-3G的空间内容完全一致,3-4G的内核空间共享
函数原型:pid_t fork(void);
功能:拷贝父进程得到一个子进程(吵架呢子进程)
参数:无
返回值:
	成功:父进程会得到子进程的PID,子进程会得到0
	失败:父进程得到-1,并置位错误码

2. 使用fork创建进程(不关注返回值)

(1. 创建单个进程

int main(int argc, char const *argv[])
{
    sleep(10);//休眠10s
    fork();
    while (1)
    
    return 0;
}

运行:
在这里插入图片描述
在前10秒休眠期,只有父进程,并处于休眠期,同时父进程PID为5975
在这里插入图片描述
10秒后,执行fork(),得到子进程PID为5990,此时main.out相当于有两个进程同时执行while(1),并且两个进程都处于运行态
在这里插入图片描述

(2. 创建多个进程

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

运行
在这里插入图片描述
可以看到产生了4个进程

  1. 当执行第一个fork:先由终端进程PID5907,产生他的子进程PID6041,执行完成后共有两个进程:进程6041,进程6042
  2. 当执行第二个fork:此时有两个进程在同时运行,分别为进程6041和他的子进程6042。他们同时运行了第二个fork,所以又各自创建了一个子进程:进程6041创建了进程6042以及进程6042创建了进程6044
  3. 最终得到的进程:进程6041,进程6042,进程6043,进程6044
    在这里插入图片描述

3. 使用fork创建进程(关注返回值)

int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork error: ");
        return -1;
    }else if(pid == 0)
    {//此部分为子进程的代码
        printf("这是子进程\r\n");
    }else
    {//此部分为父进程的代码
        printf("这是父进程\r\n");
    }
    printf("hello world\r\n");
    while (1);
    return 0;
}

运行:
在这里插入图片描述
进程:
在这里插入图片描述

4. fork出来的进程与原进程的执行先后顺序

子进程和父进程执行没有先后顺序,原因是系统进行时间片轮询机制,上下文切换原则,调度到哪个就执行哪个

int main(int argc, char const *argv[])
{
    pid_t pid = fork();

    if(pid < 0)
    {
        perror("fork error: ");
        return -1;
    }else if(pid == 0)
    {//此部分为子进程的代码
        while(1)
        printf("这是子进程\r\n");
    }else
    {
        while(1)
        printf("这是父进程\r\n");
    }

    printf("hello world\r\n");

    while (1);
    return 0;
}

5. fork出来的进程与原进程内存问题

子进程在写数据时才会创建出子进程的资源空间

6. 父子进程的进程号获取

函数原型:
	pid_t getpid(void);
	pid_t getppid(void);
功能:获取自身进程的PID或PPID
参数:无
返回值:
	不可能调用失败:成功返回进程号
============================================
int main(int argc, char const *argv[])
{
    pid_t pid = fork();

    if(pid < 0)
    {
        perror("fork error: ");
        return -1;
    }else if(pid == 0)
    {//此部分为子进程的代码
        printf("子进程PID:%d\r\n",getpid());
        printf("子进程PPID:%d\r\n",getppid());
    }else
    {
        printf("父进程PID:%d\r\n",getpid());
        printf("父进程PPID:%d\r\n",getppid());
    }

    printf("hello world\r\n");

    while (1);
    return 0;
}

运行:
在这里插入图片描述
在这里插入图片描述

7. 进程的退出(exit/_exit)

主要区别在于是否会刷新缓冲区

(1. exit

库函数:void exit(int status);
功能:结束进程,同时刷新缓冲区
参数:退出时的状态
	EXIT_FAILURE:0 成功
	EXIT_SUCCESS:1 失败
返回值:无

(2. _exit

系统调用:void _exit(int status);
功能:结束进程,不会刷新缓冲区
参数:退出时的状态
	EXIT_FAILURE:0 成功
	EXIT_SUCCESS:1 失败
返回值:无

8. 进程资源回收(wait/waitpid)

僵尸进程:子进程已结束,父进程未收尸,这子进程就是僵尸进程
孤儿进程:父进程已经结束,子进程则成了孤儿进程,被1进程收养,子进程结束后,有1进程为其收尸
1. 如果不使用wait或waitpid会产生僵尸进程
2. 所以要使用wait或waitpid回收资源

(1. wait

所需头文件:#include <sys/wait.h>

函数原型:pid_t wait(int *wstatus);
功能:阻塞等待回收子进程资源
参数1:子进程使用exit/_exit退出时的状态,一般不用接收,直接传进去NULL就行
返回值:成功返回回收了资源的子进程的pid,失败返回-1,并置位错误码
=======================================================================
int main(int argc, char const *argv[])
{
    pid_t pid = fork();

    if(pid < 0)
    {
        perror("fork error: ");
        return -1;
    }else if(pid == 0)
    {//此部分为子进程的代码
        printf("子进程PID:%d\r\n",getpid());
        printf("子进程结束\r\n");
        exit(EXIT_FAILURE);
    }else
    {
        printf("父进程PID:%d\r\n",getpid());
        wait(NULL);
        while(1);
    }

    printf("hello world\r\n");

    while (1);
    return 0;
}

运行
在这里插入图片描述
在这里插入图片描述

(2. waitpid

函数原型:pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:可以阻塞也可以不阻塞等待回收子进程号为pid的进程资源
参数1:要回收的子进程的pid号
	小于-1:回收绝对值与pid相等的同一组id下的所有子进程
	等于-1:回收任意一个子进程《常用)
	等于 0:回收多个pid中的一个,与当前进程所在的同一组的任意一个进程《常用)
	大于 0:回收值为pid的进程
参数2:子进程使用exit/ exit退出时的状态,一般不用接收,直接传进去NULL就行
参数30:阻塞等待回收资源
	WNOHANG :非阻塞等待回收资源
返回值:
	成功返回回收了资源的子进程的pid,如果是非阳塞等待,并且没有子进程结束,则返回0
	失败返回-1并置位错误码

思维导图

在这里插入图片描述

练习

1. 使用多线程拷贝文件

主函数

#include "public.h"

int main(int argc, char const *argv[])
{
    struct stat sb;
    /*获取文件属性*/
    stat("./01file.txt", &sb);//打开文件

    pid_t pid1 = fork();//创建子进程

    if(pid1 < 0)
    {
        perror("fork error: ");
        return -1;
    }else if(pid1 == 0)
    {//此部分为子进程的代码
        printf("子进程PID:%d\r\n",getpid());
        /*子进程负责上半部分的拷贝*/
        Subprocess(sb.st_size);
    }else
    {
        printf("父进程PID:%d\r\n",getpid());
        /*父进程负责下半部分的拷贝*/
        Parent_process(sb.st_size,pid1);
    }
    return 0;
}

头文件

#ifndef __PUBLIC_H_
#define __PUBLIC_H_

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/wait.h>

#define R O_RDONLY
#define W O_WRONLY|O_CREAT|O_TRUNC
#define A O_WRONLY|O_CREAT|O_ACCMODE
#define Rp O_RDWR
#define Wp O_RDWR|O_CREAT|O_TRUNC
#define Ap O_RDWR|O_CREAT|O_TRUNC

/*子进程*/
int Subprocess(int file_size);
/*父进程*/
int Parent_process(int file_size,pid_t pid);

#endif

子进程

/*子进程*/
int Subprocess(int file_size)
{
    int fp1;
    int fp2;
    int i = 0;
    char ch = 0;

    if((fp1 = open("./01file.txt",R)) == -1)
    {//文件打开失败
        perror("open file");
        return -1;
    }

    if((fp2 = open("./02file.txt",Wp)) == -1)
    {//文件打开失败
        perror("open file");
        return -1;
    }

    for(i = 0;i<(file_size/2);i++)
    {
        read(fp1,&ch,1);//读文件
        write(fp2,&ch,1);
    }
	close(fp1);
    printf("子进程任务结束 关闭进程\r\n");
    exit(EXIT_FAILURE);
}

父进程

/*父进程*/
int Parent_process(int file_size,pid_t pid)
{
    int fp1;
    int fp2;
    int i = 0;
    char ch = 0;
    int dt = file_size-(file_size/2);

    if((fp1 = open("./01file.txt",R)) == -1)
    {//文件打开失败
        perror("open file");
        return -1;
    }

    if((fp2 = open("./02file.txt",Wp)) == -1)
    {//文件打开失败
        perror("open file");
        return -1;
    }
    /*两个光标向后偏移*/
    lseek(fp1,dt,SEEK_SET);
    lseek(fp2,dt,SEEK_SET);
    for(i = 0;i<dt;i++)
    {
        read(fp1,&ch,1);//读文件
        write(fp2,&ch,1);
    }
	close(fp1);
    while (1)
    {//等待子进程结束
        if(waitpid(pid,NULL,WNOHANG )!=0 && pid!=0)
        {
            printf("子进程回收完成\r\n");
            pid=0;
            break;
        }
    }
    printf("父进程任务结束 关闭进程\r\n");
    exit(EXIT_FAILURE);
}

运行结果

在这里插入图片描述

文件1
在这里插入图片描述
文件2
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值