linux系统编程——进程

一、进程的定义

进程:运动起来的 程序。
程序是死的,只占用磁盘空间。
进程是活的,占用内存、CPU等系统资源。
1)进程是程序的一次动态执行。
2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
3)进程是具有独立功能的程序在其数据集合上运行的过程,他是系统调度和资源分配的一个独立单位。

二、进程控制块(PCB)

每个进程在内核中都有一个进程控制块来维护进程的相关信息。(打开我们的用户管理器就可以看到每一个运行的进程的相关信息)
进程的五个状态:
linux系统编程——进程_第1张图片

三、进程的创建

调用函数:fork();

pid_t fork(void)
参数:void
返回值:fork函数返回父子进程标志;
	   子进程:返回值等于0;
	   父进程:返回值大于0;
	   调用失败:返回值等于-1;
	   
获取父子进程的两个函数:
pid_t getpid()		获取当前进程id
pid_t getppid()		获取当前进程的父进程id
两个函数的返回值都是进程id

fork()函数的调用:
1)调用后存在两个进程,每个进程都会从fork()的返回值处继续执行,两个进程后各执行一次,调用之后,哪个进程先执行是无法确定的,由CPU的进程调度决定。
2)子进程的内存是对父进程的写时拷贝

//函数执行情况
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    pid_t pid;
    printf("father pid is %d\n",getpid());
    pid = fork();

    if(pid > 0){
        printf("this is father's pid print,pid is %d\n",getpid());
    }else if(pid == 0){
        printf("this is child's pid print,pid is %d\n",getpid());
    }else if(pid == -1){
        perror("fork error");
        exit(1);
    }

    return 0;
}

在这里插入图片描述

//父子进程执行情况
#include 
#include 
#include 
int main()
{
        pid_t pid1;
        pid_t pid2;
        pid1 = getpid();//打印fork()函数之前的PID
        printf("Now pid is:%d\n",getpid());

        fork();//创建fork()函数,创建子进程,执行两次
        pid2 = getpid();//获取fork()函数之后的pid
        printf("after pid is:%d\n",getpid());

        if(pid1 == pid2)//如果得到的PID和之前的PID相同,就是父进程在执行
        {
                printf("this is father's pid:%d\n",getpid());
        }else{
//如果得到的PID和之前的PID不同,就是子进程在执行
                printf("this is child's pid:%d\n",getpid());
        }
        return 0;
}

linux系统编程——进程_第2张图片

四、vfork()函与fork()函数的区别

1)子进程直接使用父进程的存储空间,不拷贝。
2)vfork()保证,子进程先执行,等子进程调用_exit()或exec()后,将暂停执行,父进程才执行。

#include 
#include 
#include 
#include 

int main()
{
    int cnt = 0;
    pid_t pid;
    pid = vfork();//vfork()创建进程
    if(pid > 0){
        while(1){
            printf("this is father's pid print,pid is %d\n",getpid());
            sleep(1);
            cnt ++;
            if(cnt == 6){
                exit(0);
            }
        }
    }
    else if(pid == 0){
        while(1){
            printf("this is child's pid print,pid is %d\n",getpid());
            sleep(1);
            cnt ++;
            if(cnt == 3){
                exit(0);
            }
        }
    }
    return 0;
}

子进程先进行3次循环,然后在轮到父进程进行循环打印三次,然后退出程序:
linux系统编程——进程_第3张图片

五、进程的使用

在电脑上发出一个请求,我们的电脑就会根据每一个请求创建一个线程,同时又在等待新的请求。

#include 
#include 
#include 

int main()
{
    pid_t pid;
    int data;
     printf("please input a data:\n");
    while(1){
        scanf("%d",&data);
        if(data == 1 || data == 6)
		//当data等于1和6的时候就可创建进程
		{
            pid = fork();
            if(pid > 0)
            {
  					//fork()函数会对父进程和子进程各执行一次,所以将父进程空白化
            }
            if(pid == 0){  //子进程处理响应
                while(1){
                    printf("yes,I get it,pid = %d\n",getpid());
                    sleep(3); //程序休眠3s防止刷屏
                }
            }
        }
        else{
            printf("wait,do nothing.\n");
        }
    }
    return 0;
}


linux系统编程——进程_第4张图片

六、exec族函数(execl、execlp、execvp)

1、exec族函数的作用
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序(父进程怕死,不希望调用新函数后自己就消失,所以用fork创建新的子进程,用exec杀死子进程来代替自己死)。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
(exec函数一旦调用就不能返回);
2、函数族
函数族:
  exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe;

#include 
extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., 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[]);

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。

参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。

编写一个execl.c文件

#include 
#include 
#include 

int main()
{
    printf("This pro get system date:\n");
    if(execl("/bin/date","date",NULL) == -1){ //打印时间
        printf("execl failed\n");
    }
    return 0;
}

运行:
在这里插入图片描述说明data在/bin/data目录下也是一个可以执行的文件。

编写一个execlp.c文件

//execlp.c
#include 
#include 
#include 
//int execlp(const char *file, const char *arg, ...);
int main()
{
    printf("before execl\n");
    if(execlp("ls","ls","-l",NULL) == -1){
        printf("execl failed\n");
    }
    printf("after execl\n");
    return 0;
}


execl()与execlp()区别就在于execlp()会在环境变量中去找可执行文件,所以我们就相当于执行了一次 ls-l 命令
linux系统编程——进程_第5张图片创建一个execvp.c文件
v指的是 应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。p指的是 从PATH环境变量中进行寻找可执行文件

//execvp.c
#include 
#include 
#include 
//int execvp(const char *file, char *const argv[]);
int main()
{
    printf("before execl\n");
    char *argv[] = {"ls","-l",NULL};
    if(execvp("ls",argv) == -1){
        printf("execl failed\n");
    }
    return 0;
}

linux系统编程——进程_第6张图片
和execlp()函数的执行结果一样。

你可能感兴趣的:(linux)