进程基本概念及相关函数

进程基本概念及相关函数

进程定义

从不同角度,进程可以有不同定义:

1.进程是程序的一次执行过程

2.进程是一个程序及其数据在处理机上顺序执行时所发生的活动。

3.进程是具有独立功能的程序在数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单元

fork函数

头文件:
#include <sys/types.h>
#include <unistd.h>

原型:
 pid_t fork(void);

功能:fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。

返回值:在理解返回值之前,我们要明确fork函数作用,fork意思是复刻
,也就意味着一个进程调用fork函数相当于又复制一份一样的,而原来的进程我们叫做父进程,新产生的进程叫子进程,返回值pid_t代表进程ID,调用一次fork函数相当于有两个返回值,父进程的fork函数返回子进程id,子进程的fork返回0

案例

//创建5个子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
    pid_t pid;
    for(int i = 0; i < 5; i++){
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }
        if(pid == 0){
            printf("i am child , pid = %d,ppid = %d\n",getpid(),getppid());
            break;
        }
    }
    return 0;
}

注意:父子进程间遵循读时共享写时复制的原则,也就是当父子进程都对一个变量只读时,这个变量的空间是一样的,而进行写操作时,则是两个空间

【重点】父子进程共享:1.文件描述符 2.mmap建立的映射区

exec函数族

exec函数族提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了,并且执行时从新程序的启动例程开始执行。
在这里插入图片描述

头文件:
#include <unistd.h>
原型:
int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
参数:
path:要执行的程序路径。可以是绝对路径或者是相对路径。在execv、execve、execl和execle这4个函数中,使用带路径名的文件名作为参数。

file:要执行的程序名称。如果该参数中包含“/”字符,则视为路径名直接执行;否则视为单独的文件名,系统将根据PATH环境变量指定的路径顺序搜索指定的文件。

argv:命令行参数的矢量数组。

envp:带有该参数的exec函数可以在调用时指定一个环境变量数组。其他不带该参数的exec函数则使用调用进程的环境变量。

arg:程序的第0个参数,即程序名自身。相当于argv[O]。

…:命令行参数列表。调用相应程序时有多少命令行参数,就需要有多少个输入参数项。注意:在使用此类函数时,在所有命令行参数的最后应该增加一个空的参数项(NULL),表明命令行参数结束。

返回值:一1表明调用exec失败,无返回表明调用成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rSbeZyJz-1585572629939)(/home/liuchenxu/.config/Typora/typora-user-images/1585572542873.png)]

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("========================\n");

    char *argvv[] = {"ls", "-l", "-F", "R", "-a", NULL};
    

    pid_t pid = fork();
    if (pid == 0) {
        execl("/bin/ls", "ls", "-l", "-F", "-a", NULL);
        //while是一个可执行程序
        execl("./while","while",NULL);
        execv("/bin/ls", argvv);
        perror("execlp");
        exit(1);

    } else if (pid > 0) {
        sleep(1);
        printf("parent\n");
    }


    return 0;
}

wait函数

孤儿进程
父进程先于子进程结束, 孤儿进程被init进程领养, init进程变为孤儿进程的父亲
这是为了释放进程占用的系统资源,进程被接收之后, 能够释放用户空间,释放不了pcb, 必须由父进程释放
僵尸进程
子进程死了, 父进程还活着, 父进程不去释放子进程的pcb, 子进程就变成了僵尸进程

头文件:
#include<sys/types.h>
#include<sys/wait.h>
原型:
pid_t wait (int * status);
功能:
父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一
个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程
,wait就会一直阻塞在这里,直到有一个出现为止。
参数:
参数 status 是一个整形指针。如果status不是一个空指针,则终止进程的终止状态将存储在该指针所指向的内存单元中。
如果不关心终止状态,可以将 status参数设置为NULL。
返回值:
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。

在这里插入图片描述
注意:使用wait函数一次只能回收一个子进程

waitpid函数

头文件:
#include<sys/types.h>
#include<sys/wait.h>
原型:
 pid_t waitpid(pid_t pid,int * status,int options);
 功能:如果在调用 waitpid()时子进程已经结束,waitpid()会立即返回子进程结束状态值。
  子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则
参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码。
参数:
pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于 wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为 pid 的子进程。
参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);如果我们不想使用它们,也可以把options设为0
返回值:如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回值-1。失败原因存于 errno 中。
发布了38 篇原创文章 · 获赞 39 · 访问量 4449

猜你喜欢

转载自blog.csdn.net/qq_43799957/article/details/105208223