多任务

多任务主要包括:进程、进程间通信、线程这三大内容

一、什么是单任务?
一个任务执行结束之后,另一个任务才开始执行。

二、什么是多任务?
1)正确理解:系统可以响应多个任务。一个任务执行过程中,可以暂停执行,然后cpu执行其他任务。
2)错误理解:同时执行多个程序。
接下来,解释一下为什么这个说法是错误的。对于单个cpu平台来说,也就是单核平台来说,一个核同一时刻只能执行一条语句,但是单核也可以进行多任务。这就说明多任务不可能同时执行多个程序。
2、单核和多核cpu进行多任务时,单核是并发执行,多核是并行执行
1)并发执行(单核)——就是cpu轮换的执行,当前进程执行了一个短暂的时间片之后,切换执行另一个程序,如此反复,在宏观上感觉到所有的进程是在同时运行的,但是在微观上cpu每次只执行某一个进程的命令。
并行执行(多核)——不同的cpu同时独立执行不同的程序(与单核多任务区分开),这种叫并行执行。所以在多核中,并发核并行是同时存在的。
3、多任务:在进行任务切换时(任务切换是由操作系统来捕获的),分为抢占式和非抢占式
1)时间片轮转:非抢占式,非实时性,给每个任务使用cpu固定的时间,单核cpu进行多任务的方式。
2)中断:抢占式,实时性高。

进程

1、多任务编程也就是多进程、多线程编程
2、什么是进程?(进程和程序)
所有的程序都在硬盘上,只有./a.out执行这个程序时,它才会被加载到内存中,这叫做进程,程序执行结束后,进程也就随之而消失。程序是静态的,进程是动态的。

进程:一个程序执行的一次过程,进程是操作系统执行的最小执行单元,一个程序会有多个进程。

3、linux进程特点?

每个进程都有自己独立的虚拟地址空间,大小为4G(互不干扰,相互独立),所有的数据保存在自己独立的空间内。

4、进程虚拟地址空间的划分?

多任务中个任务权重配置_多任务中个任务权重配置


多任务中个任务权重配置_多任务_02


从上往下,高地址——低地址

栈空间:先进后出,系统管理,生命周期由所在函数决定

堆空间:先进先出,用户管理(malloc,free),用户决定

数据段:系统管理,生命周期:全局

5、进程id:动态分配,获取id
6、所有进程创建都是由父进程创建的,父进程创建的进程称为子进程
7、Linux下所有进程的父进程:init 0

创建进程

进程的创建1——fork函数
1、fork函数作用是:创建进程
2、工作原理
1)给子进程分配空间
2)拷贝父进程的代码段,数据段到子进程的地址空间
3)给父子进程的pid赋值

fork函数创建的进程,父子进程的执行顺序是随机的,父子进程的调度是由系统决定的,父子进程有自己的各自空间

3、getpid函数返回当前进程标识

getppid函数返回父进程标识

4、返回值作用:父进程返回的id号其实就是子进程的pid号

多任务中个任务权重配置_子进程_03

进程的创建2——vfork函数

执行顺序:创建子进程,子进程先执行,然后再执行父进程

空间:父子进程享用同一块空间

注意点:释放空间只能释放一次,所以在子进程结束后,要使用_exit()函数异常退出,否则会发生内存错误。

多任务中个任务权重配置_父进程_04


进程的创建3——system和exec函数族

1、system创建进程:也就是在进程中运行另一个程序

2、system(“./程序”),所以需要先将.c文件链接成可执行文件,gcc 文件名.c -o 文件名

3、exec函数族:覆盖原有进程的代码段,创建新的进程,pid会发生改变

4、system和exec区别:system执行的进程不会覆盖原有的进程代码,也就是说不会改变新的进程,pid不会发生改变

多任务中个任务权重配置_父进程_05

进程退出

exit或者_exit函数

区别:
1、exit是库函数,里面封装了_exit函数,_exit函数是系统调用函数,只能在Linux下使用
2、_exit函数退出进程不会使进程刷新缓冲区,而exit函数在_exit函数后会刷新缓冲区。

进程等待

1 、僵尸进程:父进程先于子进程退出来,该子进程就成了一个僵尸进程,因为子进程的资源需要靠父进程进行回收。
2、怎么解决僵尸进程?
1)进程等待,等待子进程结束后回收资源
2)wait(pid)函数堵塞等待子进程退出
3)waitpid函数

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
//waitpid的返回值比wait稍微复杂一些,一共有3种情况:
// 1、当正常返回的时候,waitpid返回收集到的子进程的进程ID;
// 2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
// 3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

//WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回
// pid>0时,只等待进程ID等于pid的子进程
// pid=-1时,等待任何一个子进程退出,没有任何限制
// pid=0时,等待同一个进程组中的任何子进程,
//如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
// pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

int main()
{
    //pid_t waitpid(pid_t pid,int *status,int options);
    pid_t pc,pr;
    pc=fork();
    if(pc<0)
    {
    	 printf("error occured on forking\n");
    }
    else if(pc==0)
    {
    	printf("fork jincheng success\n");
        sleep(5);
        exit(0);
     }
       /* 如果是父进程 */
     do{
     	  pr=waitpid(pc,NULL,WNOHANG);// 使用了WNOHANG参数,waitpid不会在这里等待
          if(pr==0)
           {
                printf("no child exited\n");
                sleep(1);
            }
        }whlie(pr==0);/* 没有收集到子进程,就回去继续尝试 */
         if(pr==pc)
    {
        printf("successfully get child %d\n",pr);
    }
    else
    {
        printf("some errors occured\n");
    }
    
    return 0;
}
//把pr=waitpid(pc, NULL, WNOHANG)
//改为pr=waitpid(pc, NULL, 0)或者pr=wait(NULL)
//修改后的结果使得父进程将自己阻塞,直到有子进程退出为止