进程的控制

进程的定义:正在运行的一段程序,说明了它是一个动态的过程。进一步解释,进程开始的时候,启动了一个动态过程,它主要包括两个部分:执行程序  数据
1.动态性
进程是程序的一次执行过程,他从动态产生到动态消亡
2.并发性--重点
进程具有并发的特性,从宏观上看它们是同时执行的,

但是微观上看,由于CPU同一时间只能执行一个进程对应的代码,

所以这些进程在微观上是交替运行的,并发指的是CPU在这些进程之间进行快速切换,让用户感觉到时同时运行的。
时间片---根据系统的时钟  2.4*1024M   168m 

3.独立性
第一个:进程是系统进行资源分配的最小单位
第二个:进程都具有唯一的标识,用非负整数表示进程的ID,这个ID就是进程的标识符,作用:就如同身份证,具有唯一性。
补充:表示的类型pid_t 本质是一个无符号的整数---重点
4.异步性
进程之间可以互相制约,使得进程之间产生间隙

5.结构性
进程的结构:主要包括:进程控制块,数据,程序,主要由这三部分组成
进程控制块:(PCB)是系统为了管理进程专门设立了一个数据结构,

作用:系统能够用它来记录进程外部特征
这些特征描述进程的动态变化过程,同时系统可以利用进程控制块控制和管理进程,因此PCB--进程控制块是系统感知进程存在的唯一标识
PCB(进程控制块)主要组成部分
1.程序计数器:主要包括运行指令的地址
2.进程的状态:new running waiting blocked等,主要作用就是描述当前的状态
3.存取器管理,如标签页,表等
4.输入和输出的状态:配置进程中使用到的IO设置;例如:打印机、磁带机
进程和程序的区别
第一个:程序可以以文件的形式保存在内存中(占用的存储器中),而进程只能运行在系统中。
第二个:进程是一个动态的过程,程序只是一个静态的概念。
第三个:进程和程序之间的对应关系,未必是一一对应的,一个程序可以对应多个进程。
进程的状态:
进程的状态分为三种:
进程中的就绪态:当进程已经分配到cpu之外的所有资源,只要获取cpu就会立即执行,这种状态称为就绪态。
执行状态:程序正在使用CPU,能够正常的按照代码的循序进行执行,此时的进程已经获取了CPU,此时进程状态称为执行态
阻塞态:进程被挂起,处于一个等待的状态。  阻塞态的定义:正在执行的程序,由于要等待某个事件的发生而无法继续执行,便放弃执行的机会处于一个阻塞的状态
注意:引起进程处于阻塞状态的的条件
第一个:有等待IO完成。   打印机
第二个:申请一个缓存区
第三个等待条件(信号)满足等
各进程状态的切换:
进程中就绪状态,执行状态,阻塞状态如何进行切换
注意事项:进程处于阻塞状态的时候,如果说获得了事件请求,只能回到就绪态,不能直接回到运行态。
补充区别:进程概念和USCOLL任务的最大区别,Ucoll中任务是子函数(不是main函数),因此,uscoll任务只是属于一个线程,而进程一定会是一个MAIN函数
进程相关命令:
PS查看进程:一般使用ps -aux查询当前系统下所有进程的一个状态或者ps -cf查询当前系统下父子进程关系。
ps命令查看当前进程的状态,进程状态标记的含义?
第一个:R:表示正在进行的进程
第二个:S:表示阻塞状态
第三个:T:表示暂停状态
第四个:Z:进程运行结束被收回--僵尸态
第五个:I:表示多进程
第六个:+:表示后台运行的进程组
kill消灭进程
一般使用 kill -9 pid 消灭对应的进程--注意使用-9表示强制杀死进程
kill命令的本质:就是给一个指定的进程发送一个信号。
特殊:利用kill命令-- kill -9 I 相当于杀死了所有进程
 父子进程:
 大多数情况下进程是不能够凭空产生的,往往需要另外一个进程来产生的,此时这两个进程之间的关系属于父子进程,其中父子进程中,最顶层父进程属于祖先进程。
 注意:子进程运行结束后,需要父进程收回空间,目的防止僵尸进程的产生

 祖先进程:
 计算机启动的时候,BOSS从硬盘上引导加载系统程序,并将Linux系统装入到对应的内存中,内核需要进行初始化,建立一个进程0;
 进程0再创建进程1是以后创建所有进程的祖先进程(pid=1)进程1负责对所有进程的初始化和管理,以及shell进程也是由进程1创建的

 守护进程:
 守护进程又称为精灵进程,运行在后台的一种特殊进程,特点独立于控制终端并周期的执行某个任务
 僵尸进程:
 僵尸进程占用系统资源,确不能有效的执行进程称为僵尸进程,相当于系统内部的垃圾,会对系统产生副作用、产生过程:父进程没有结束,子进程结束了。父进程没有调用wait()或者waitpid()
 孤儿进程:
 在子进程结束之前,父进程已经结束,此时子进程称为孤儿进程,会被祖先“收养”,子进程运行结束之后,有祖先进程将空间收回。

头文件:
 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//第一个:getpid()获取一个进程的pid号
原型:pid_t getpid(void)
返回值:成功:当前进程的id号
//第二个:getppid()获得当前进程父进程的id号
原型:pid_t getppid(void)
返回值:成功:当前父进程的id号

//1.练习:获取当前进程的id和父进程的pid
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    pid_t pid,ppid;
    pid = getpid();
    ppid = getppid();
    printf("pid=%d\r\n ppid=%d\r\n",pid,ppid);
    while(1);
}


函数:system()功能:在一个运行进程中打开另外一个进程,运行完成之后回到原来的进程继续执行。
//函数:运行进程
原型:int system(const char*string)
参数:const char*string---》表示待执行的命令,例如:ls -ld
int a;a=system("./a.out");printf("%d\r\n",a);
功能:函数system()会调用fork()函数子进程,由于进程调用了string字符串命令,执行命令之后重新返回。
练习二:函数system()函数实现调用进程id,完成之后重新返回,执行“pwd”
思路:第一步:先判断本进程的PID和PPID
第二步:调用SYSTEM()实现A.C的进程PID和PPID
第三步:调用SYSTEM()实现打印当前路径,打印“ls -l”

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    pid_t pid,ppid;
    pid = getpid();
    ppid = getppid();
    printf("pid=%d\r\n ppid=%d\r\n",pid,ppid);
    system("./a.out");
    system("pwd");
}

替换进程:
函数体exec()
功能:使用新进程替换当前进程的程序---注意:进程ID不变,变化的只是执行代码的变化
函数:替换进程exec()函数----相当于一个函数,函数名中出现execl() v,e,p结尾
原型:int execl(const char*path,char *arg....)
参数:const char*path,--》执行程序的路径
char *arg....》表示传进来的参数,还要加上NULL
返回值:成功:不会返回,也就是说,execl()函数之后的代码不会执行
失败:返回-1;
功能:用参数指定的进程替换当前的进程,并PID号不发生改变

思路:第一个:先输出当前的PID和PPID
第二步:通过替换进程检查一下PID和PPID是否发生变化
例子:execl("./test","./test",NULL);

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid,ppid;
    pid = getpid();
    ppid = getppid();
    printf("pid=%d\r\n ppid=%d\r\n",pid,ppid);
    execl("./test1","./test1",NULL);
    printf("hello\r\n");
    
}

system()与execl()区别,不同点有哪些
第一个:调用SYSTEM函数的时候,他创建一个新进程,当这个新进程执行完成的时候,重新返回到原来的执行的地方继续执行,调用execl()函数的时候,新进程替换掉你当前的一个进程,因此execl()之后的代码不会得到执行。
第二个:这两个函数都会进入到新进程里面执行。
=========================================
创建进程的函数fork()
返回值:在父进程中,用fork()返回新创建子进程的进程ID
在子进程中,fork()函数返回值为0
失败的情况 返回一个负值

//练习:操作对应的fork()函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid,ppid;
    int i=0;
    fork();
    i++;
    printf("i=%d\r\n",i);
    printf("pid=%d\r\n ppid=%d\r\n",pid = getpid(),ppid = getppid());
    
}
//两次输出变量I均为1。
//练习二:实现父子进程中分别执行不同的代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid,ppid,ret;
    int i=0;
    ret = fork();
    if(ret < 0)
    {
        printf("creat child process failed\r\n");
        return -1;
    }
    if(ret == 0)
    {
        //子进程
        printf("i am child pid = %d\r\n ppid=%d\r\n",getpid(),getppid());
    }
    else
    {
        //父进程
        printf("i am parent pid = %d\r\n ppid=%d\r\n",getpid(),getppid());
        sleep(1);
    }
}    


fork()进程执行分析
第一个:父进程一般是先执行的进程,再子进程但是这种情况下会导致父进程先子进程结束,导致子进程称为孤儿进程,可能无法达到预期的执行效果。
第二个:父子进程运行的时候,如何进程切换
通常情况下,让能处于运行的进程被阻塞,这个时候调度器回去运行另外一个进程
补充:阻塞如何实现?
第一种休眠等待---利用sleep()函数
原型:unsigned int sleep(unsigned int seconds)
参数:unsigned int seconds--》代表休眠的秒数
功能:让调用该函数的进程等待多少秒

usleep()
原型:int usleep(useconds_t usec)
参数:useconds_t usec--》代表休眠的微秒数
功能:让调用该函数的进程等待多少微秒

第二种:IO阻塞
例如:scanf()--会让进程进入阻塞状态
第三种:时间片完成

//小项目:实现父子进程相互报数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid,ppid,ret;
    int i;
    ret = fork();
    if(ret < 0)
    {
        printf("creat child process failed\r\n");
        return -1;
    }
    if(ret == 0)
    {
        //子进程
        int i;//子进程的变量空间是独立的
        for(i=2;i<=26;i+=2)
        {
            sleep(1);    
            printf("pid = %d\r\n ppid=%d\r\n",getpid(),getppid());
            sleep(1);
        }
    }
    else
    {
        //父进程
        int i;
        for(i=1;i< 26;i+=2)
        {
            printf("parent pid = %d\r\n ppid=%d\r\n",getpid(),getppid());
            sleep(2);
        }
    }
}
//补充功能:父子进程实现多进程控制---实现框架
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid,ppid,ret;
    int i;
    ret = fork();
    if(ret < 0)
    {
        printf("creat child process failed\r\n");
        return -1;
    }
    if(ret == 0)
    {
        //子进程要执行的代码,可以定义变量
        int i;//子进程的变量空间是独立的
        for(i=2;i<=26;i+=2)
        {
            sleep(1);    
            printf("pid = %d\r\n ppid=%d\r\n",getpid(),getppid());
            sleep(1);
        }
    }
    else
    {
        //代表父进程可执行的代码,也可以定义父进程的变量
        ret2 = fork()//---实现第二次创建子进程
        if(ret == 2)
        {
            //第二个子进程运行的代码
        }
        else
        {
            //父进程
            
        }
    }
}


//fork()函数,创建子进程
原型:pid_t vfork(void)
返回值:和fork()函数一样,成功,子进程返回0
功能:创建一个子进程并阻塞父进程的运行直到子进程运行结束

//练习:输出子进程的PID PPID再加入变量,也输出父进程的PID PPID再加入一个变量
//思路:创建子进程,接招判断是否成功,父子进程执行相应的代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid,ppid,ret;
    int i;
    ret = vfork();
    //判断vfork是否正常执行
    if(ret < 0)
    {
        printf("creat child process failed\r\n");
        return -1;
    }
    if(ret == 0)
    {
        //子进程要执行的代码,可以定义变量
        i++;    
        printf("child \r\n chile = %d\r\n pid = %d\r\n ppid=%d\r\n",i,getpid(),getppid());
        sleep(1);
        printf("child2\r\n");
        exit(0);
    }
    else
    {
        //父进程,内核阻塞,父进程被挂起
        i++;
        printf("parent\r\n");
        printf("parent=%d\r\n",i);
        printf("pid=%d\r\n ppid=%d\r\n",getpid(),getppid());
        
    }
    exit(0);
}

总结:第一个:共同点:都可以创建一个子进程,并具有相同的返回值
第二个:不同点,fork()函数,父子进程的运行循序是可以调整的,而vfork()函数的执行顺序不能调整,必须等待子进程运行结束
第三个:不同fork()执行签,父子进程空间是独立的,而vfork执行前父子空间是公用的
补充:return 功能:结束一个函数,并不代表结束一个进程
exit()函数:结束一个进程,整个程序相当于结束
================================================
进程在哪些情况下会被销毁?
第一个:进程运行代码执行结束  ---正常退出。
第二个:进程运行的时候,调用了退出函数,例如:exit()和_exit()---正常退出
第三个:使用了ctrl键+c键 结束了当前的进程。还可以通过kill强制杀死对应的进程。
第四个:进程运行出错的时候,也会导致进程销毁。 段错误。
====================================================
退出函数:exit()
原型:void exit(int status)
参数:int status   ---用于返回数据给父进程,一般填写为0
功能:exit()函数正常结束进程,并把参数返回给父进程,会将进程缓冲区的内容全部写回并关闭文件

原型:void _exit(int status)
参数:int status   ------用于返回数据给父进程,一般填写为0
功能:_exit()正常终止目前正在执行的进程,但是不会将缓存区内容写回

exit()和_exit()区别
主要区别:exit()函数会将换冲区的内容进行写回。注意:如果满足缓冲区条件也可以正常输出。
原型:pid_t wait(int *status)   --exit(0)
参数:int *status --->用于接收子进程退出时候返回给父进程数据
返回值:子进程的PID
功能:wait()会暂时停止目前执行的进程,直到接收到子进程结束的信号。如果调用wait()函数,子进程已经结束
返回给父进程子进程结束的状态信息。如果说不知道子进程结束的状态,设置为NULL

实例:功能:通过父进程,利用wait()函数,打印子进程的进程号PID

思路:父进程等待子进程结束,接收子进程的PID,并打印

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>

int main(void)
{
    pid_t pid,ppid;
    int i =0 ;
    pid_t ret;
    ret = fork();
    if(ret < 0)
    {
        printf("creat child process failed\r\n");
        return -1;    
    }
    if(ret == 0)
    {
        printf("I am child pid = %d, ppid = %d\r\n",getpid(),getppid());
        sleep(3);
        printf("child\r\n");
    }
    else
    {
        pid_t c_pid;
        printf("parents\r\n");
        c_pid = wait(NULL);
        printf("I am parents pid = %d,ppid = %d\r\n",getpid(),getppid());
        printf("Parents :my child PID is :%d\r\n",c_pid);
    }
    exit(0);
    
}


原型: pid_t waitpid(pid_t pid,int *status,int options);
参数: pid_t pid --->指定等待的进程PID号
         如果:pid=-1  等待任何子进程结束  wait()函数功能
         如果: pid>0   等待相应的子进程结束
     options   表示选项参数,一般设置为0

功能:和wait函数基本一致,但是可以等待指定的进程。
wait()和waitpid()在进程中的作用
第一个:用于父进程等待子进程结束,避免孤儿进程的产生。
第二个:在等待子进程运行结束后,该函数实现清零收回子进程空间,避免僵尸僵尸进程的产生。
练习:构建一个父进程两个子进程,实现三个进程报数1~10,要求父进程必须要在两个进程退出后再退出。
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>

int main(void)
{
    pid_t ret;
    ret = fork();
    if(ret < 0)
    {
        //错误信息
        
        return -1;
    }
    if(ret == 0)
    {
        //child1
        
    }
    else
    {
        //parents
        //fork()
        
        wait(NULL);
    }
        exit(0);
}
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值