Linxu系统编程——进程

Linxu系统编程——进程

1.进程相关概念

2.创建进程函数fork的使用

3.创建进程函数fork的使用补充

4.进程创建发生了什么

5.创建新进程的实际应用场景及fork总结

6.vfork创建进程

7.进程退出

8.父进程等待子进程退出(一)

9.创建进程函数fork的使用(二)

10.exec族函数

11.exec族函数配合fork使用

12.system函数

13.popen函数

14.进程总结

1.进程相关概念

问1. 什么是程序,什么是进程,有什么区别?

  • 程序是静态的概念,gcc xxx.c –o pro

  • 磁盘中生成pro文件,叫做程序

  • 进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程

问2. 如何查看系统中有哪些进程?

  • 使用ps指令查看 ps -aux

    ​ 实际工作中,配合grep来查找程序中是否存在某一个进程

  • 使用top指令查看,类似windows任务管理器

问3. 什么是进程标识符?

  • 每个进程都有一个非负整数表示的唯一ID,

    叫做pid,类似身份证

    Pid=0: 称为交换进程(swapper)

    作用—进程调度

    Pid=1:init进程

    作用—系统初始化

    • 编程调用getpid函数获取自身的进程标识符

    getppid获取父进程的进程标识符

问4. 什么叫父进程,什么叫子进程?

  • 进程A创建了进程B

    那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系

问5. C程序的存储空间是如何分配?

在这里插入图片描述

在这里插入图片描述

2.创建进程函数fork的使用

使用fork函数创建一个进程

pid_t fork(void);

fork函数调用成功,返回两次

返回值为0, 代表当前进程是子进程

返回值非负数,代表当前进程为父进程

调用失败,返回-1

demo2

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

int main()
{
        pid_t pid;
        pid = getpid();
        fork();
        printf("my pid is %d\n",pid);

        return 0;
}

在这里插入图片描述

demo3

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

int main()
{
        pid_t pid;
        pid = getpid();
        fork();
        printf("my pid is %d,current pro is %d\n",pid,getpid());

        return 0;
}

在这里插入图片描述

demo4

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

int main()
{
        pid_t pid;
        pid = getpid();
        fork();
        if(pid == getpid()){
                printf("this is father pid\n");
        }else{
                printf("this is child pid= %d\n",getpid());
        }
        return 0;
}

在这里插入图片描述

demo5

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

int main()
{
        pid_t pid;
        printf("father pid = %d\n",getpid());
        pid = getpid();
        printf("get  pid = %d\n",pid);
        fork();
        if(pid>0) {
                printf("this is father pid= %d\n",getpid());
        }else if(pid == 0){
                printf("this is child pid= %d\n",getpid());
        }
        return 0;
}

在这里插入图片描述

3.创建进程函数fork的使用补充

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

int main()
{
        pid_t pid;
        pid_t pid2;
        pid_t retpid;
        pid = getpid();
        printf("before fork:pid = %d\n",pid);

        retpid = fork();
        pid2 = getpid();
        printf("after fork : pid = %d\n",pid2);
        if(pid == pid2){
                printf("this is father pid:%d ,retpid=%d\n",getpid(),retpid);
        }else{
                printf("this is child pid= %d,retpid = %d\n",getpid(),retpid);
        }
        return 0;
}

在这里插入图片描述

4.进程创建发生了什么

在这里插入图片描述

当fork后;原来的linux会全拷贝;后期用的写时拷贝(copy-on-write),共享内存,进行只读拷贝,只有对数据修改时,才会在子进程的地址空间拷贝修改

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

int main()
{
        pid_t pid;
        pid_t pid2;
        pid_t retpid;
        pid = getpid();
        int data = 10;
        printf("before fork:pid = %d\n",pid);

        retpid = fork();
        pid2 = getpid();
        if(pid == pid2){
                printf("this is father pid:%d ,retpid=%d\n",getpid(),retpid);
        }else{
                printf("this is child pid= %d,retpid = %d\n",getpid(),retpid);
                data += 100;
        }
        printf("data = %d\n",data);
        return 0;
}

输出结果:
在这里插入图片描述

5.创建新进程的实际应用场景及fork总结

在这里插入图片描述

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

int main()
{
        pid_t pid;
        int data = 0;

        while(1){
        printf("please inter data\n");
        scanf("%d",&data);
                if(data == 1){		//当输入1时,创建子进程
                        pid = fork();
                        if(pid >0){		//父进程什么都不做
		
                        }else if(pid ==  0){
                                while(1){	
                                printf("this is child pid= %d\n",getpid());		//子进程打印进程ID
                                sleep(3);
                                }
                        }
                }else{
                        printf("waiting\n");
                }
        }

        printf("data = %d\n",data);
        return 0;
}

输出结果:
在这里插入图片描述

6.vfork创建进程

在这里插入图片描述

fork函数使用现象:

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

int main()
{
        pid_t pid;

        pid = fork();
        if(pid >0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                }
        }else if(pid ==  0){
                while(1){
                printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        }
             }
        return 0;
}

输出结果:
在这里插入图片描述

两个进程相互争夺CP资源,

vfork函数使用现象:

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

int main()
{
        pid_t pid;
        int cnt = 0;
        pid = vfork();
        if(pid >0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        printf("%d\n",cnt);
                }
        }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(0);//  0:退出状态 让子进程正常退出(保证子进程正常退出,防止破坏cnt)
                        }
                }
           }
        return 0;
}

输出结果:
在这里插入图片描述

vfork函数使用的是父进程存储空间,不拷贝;而且保证子进程先运行,只有等子进程调用exit(0),正常退出(保证cnt值不被破坏);父进程才执行

7.进程退出

在这里插入图片描述

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

8.父进程等待子进程退出(一)

在这里插入图片描述

僵尸进程:

  1. 子进程退出状态不被收集,变成僵死进程(僵尸进程)
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
        pid_t pid;
        int cnt = 0;
        pid = vfork();
        if(pid >0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        printf("%d\n",cnt);
                }
        }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(0);
                        }
                }
           }
        return 0;
}

结果:
在这里插入图片描述

  1. 父进程等待子进程退出并收集子进程的退出状态
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
        pid_t pid;
        int cnt = 0;
        pid = fork();
        if(pid >0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        printf("%d\n",cnt);
                }
        }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(0);
                        }
                }
           }
        return 0;
}

输出结果:
在这里插入图片描述

wait函数

在这里插入图片描述

在这里插入图片描述

使用wait函数,等待子进程运行完,对子进程退出状态收集,防止子进程出现僵尸进程

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

int main()
{
        pid_t pid;
        int cnt = 0;
        pid = fork();
        if(pid >0){
                wait(NULL);		//等待子进程运行完;NULL不关心返回值
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        printf("%d\n",cnt);
                }
        }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(0);
                        }
                }
           }
        return 0;
}

输出结果:
在这里插入图片描述

正常退出,获取返回值

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

int main()
{
        pid_t pid;
        int cnt = 0;
        int status = 6;
        pid = fork();
        if(pid >0){
                wait(&status);
                printf("child quit,child status:%d\n",WEXITSTATUS(status));	//WEXITSTATUS(status):若为正常终止子进程返回的状态,则																			//为真。对于这种情况可执行WEXITSTATUS(status),取子进程传送																			//给exit、_exit或Exit参数的低8位
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        printf("%d\n",cnt);
                }
        }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(5);
                        }
                }
           }
        return 0;
}

输出结果:
在这里插入图片描述

9.父进程等待子进程退出(二)

waitpid函数

在这里插入图片描述

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

int main()
{
        pid_t pid;
        int cnt = 0;
        int status = 6;
        pid = fork();
        if(pid >0){
              //  wait(&status);
            	waitpid(pid,&status,WNOHANG); 	//pid是子进程的,不阻塞 ;非阻塞等待,子进程也会成为僵尸进程
                printf("child quit,child status:%d\n",WEXITSTATUS(status));	//WEXITSTATUS(status):若为正常终止子进程返回的状态,则																			//为真。对于这种情况可执行WEXITSTATUS(status),取子进程传送																			//给exit、_exit或Exit参数的低8位
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        printf("%d\n",cnt);
                }
        }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d\n",getpid());
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(5);
                        }
                }
           }
        return 0;
}

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

孤儿进程

在这里插入图片描述

实现:

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

int main()
{
        pid_t pid;
        int cnt = 0;
        int status = 6;
        pid = fork();
        if(pid >0){
                        printf("this is child pid= %d\n",getpid());
                }else if(pid ==  0){
                while(1){
                        printf("this is child pid= %d,father pid = %d\n",getpid(),getppid()); //getppid()在子进程里面获取父进程id,init进程id为1
                        sleep(3);
                        cnt++;
                        if(cnt == 3){
                                exit(5);
                        }
                }
           }
        return 0;
}

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

10.exec族函数

exec族函数讲解博文

exec族函数函数的作用:

我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

exec族函数定义:
  可以通过这个网站查询:linux函数查询
功能:
  在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
  exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe
函数原型:

#include <unistd.h>
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函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

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

pwd:显示路径

添加环境变量:export PATH=$PATH:路径

//文件echoarg.c
#include <stdio.h>

int main(int argc,char *argv[])
{
    int i = 0;
    for(i = 0; i < argc; i++)
    {
        printf("argv[%d]: %s\n",i,argv[i]); 
    }
    return 0;
}

execl函数

失败案列:(原因:路径不对)

//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    printf("before execl\n");
    if(execl("./bin/echoarg","echoarg","abc",NULL) == -1)   //第一个参数就是添加要执行程序名和路径,第二个参数为执行程序名,第三个为执行程序所代参                                                                                                                //数(无写NULL),第四个是要以NULLL结束
    {
        printf("execl failed!\n");
        perror("why");		//打印运行失败的原因
        }
    printf("after execl\n");
    return 0;
}

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

成功案列:

//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    printf("before execl\n");
    if(execl("./echoarg","echoarg","abc",NULL) == -1)   //第一个参数就是添加要执行程序名和路径,第二个参数为执行程序名,第三个为执行程序所代参														//数(无写NULL),第四个是要以NULLL结束
    {
        printf("execl failed!\n");      
    }
    printf("after execl\n");
    return 0;
}

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

失败和成功在于执行程序文件目录,运行成功后,不会执行之前未执行的代码

execlp函数

//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execlp(const char *path, const char *arg, ...);

int main(void)
{
    printf("before execl\n");
    if(execlp("ls","ls","-l",NULL) == -1)   //第一个参数就是添加要执行程序名和路径,第二个参数为执行程序名,第三个为执行程序所代参                                                                                      //数(>无写NULL),第四个是要以NULLL结束
    {
        printf("execl failed!\n");
    }
    printf("after execl\n");
    return 0;
}

whereis ls查找ls目录

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

tips:execl 和 execlp 的区别在于有没有路径名,execl需要添加路径名,execlp直接在环境变量中查抄可执行文件名

execv函数

//文件execvp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execvp(const char *path, char *const argv[]);

int main(void)
{
    printf("before execvp****\n");
    char *argv[] = {"echoarg",NULL,NULL};
    if(execv("./echoarg",argv) == -1)
    {
        printf("execvp failed!\n");
    }
    printf("after execvp*****\n");
    return 0;
}

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

execvp函数

//文件execvp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execvp(const char *file, char *const argv[]);

int main(void)
{
    printf("before execvp****\n");
    char *argv[] = {"ls","-l",NULL};
    if(execv("ls",argv) == -1)
    {
        printf("execvp failed!\n");
    }
    printf("after execvp*****\n");
    return 0;
}

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

11.exec族函数配合fork使用

使用:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
        pid_t pid;
        int data = 0;

        while(1){
        printf("please inter data\n");
        scanf("%d",&data);
                if(data == 1){
                        pid = fork();
                        if(pid > 0){
                                wait(NULL);
                                exit(0);
                        }else  if(pid ==  0){
                                         int fdSrc;
                                         char *readbuf = NULL;
                                         fdSrc = open("./config.txt",O_RDWR);  //打开txt文件
                                         int fdSrcSize = lseek(fdSrc,0,SEEK_END); //获得文件大小
                                         lseek(fdSrc,0,SEEK_SET);			//光标定位到文件头
                                         readbuf = (char *)malloc(sizeof(char)*fdSrcSize+8);	//根据获得大小开辟一个缓冲区
                                         int n_read = read(fdSrc,readbuf,fdSrcSize);	//读取内容到缓冲区
                                         char *p = strstr(readbuf,"LENG=");		//找到第一次需要查找的内容头部
                                         if( p == NULL){
                                                 printf("No found\n");
                                                 exit(-1);
                                         }
                                         p = p + strlen("LENG=");	
                                         *p='5';
                                         lseek(fdSrc,0,SEEK_SET);
                                         int n_write  = write(fdSrc,readbuf,strlen(readbuf));

                                          close(fdSrc);
                                        printf("change success!\n");
                                         exit(0);
                                }
                }else{
                        printf("waiting\n");
                }
        }

        return 0;
}

程序运行前config.txt文件内容:
在这里插入图片描述

程序运行后config.txt文件内容:
在这里插入图片描述

exec族函数配合fork函数使用:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
        pid_t pid;
        int data = 0;

        while(1){
        printf("please inter data\n");
        scanf("%d",&data);
                if(data == 1){
                        pid = fork();
                        if(pid > 0){
                                wait(NULL);
                                exit(0);
                        }else  if(pid ==  0){
                                        execl("./changeData","changeData","config.txt",NULL);
                                        printf("change success!\n");
                                         exit(0);
                                }
                }else{
                        printf("waiting\n");
                }
        }

        return 0;
}

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

12.system函数

在这里插入图片描述

讲解博文

system()函数的返回值如下:

成功,则返回进程的状态值;

当sh不能执行时,返回127;

失败返回-1;

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
        pid_t pid;
        int data = 0;
        while(1){
        printf("please inter data\n");
        scanf("%d",&data);
                if(data == 1){
                        pid = fork();
                        if(pid > 0){
                                wait(NULL);
                                exit(0);
                        }else  if(pid ==  0){
                        //              execl("./changeData","changeData","config.txt",NULL);   
                                        system("./changeData config.txt");
                                        printf("change success!\n");
                                         exit(0);
                                }
                }else{
                        printf("waiting\n");
                }
        }

        return 0;
}

程序运行结果:
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
    printf("before execl\n");
    if(system("ls") == -1)
    {
        printf("execl failed!\n");
        perror("why");
        }
    printf("after execl\n");
    return 0;
}

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

13.popen函数

在这里插入图片描述
mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。

讲解博文

比system在应用中的好处:

可以获取运行的输出结果

使用system没有返回值:

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

int main(void)
{
    FILE *fp;
    char ret[1024] = {"0"};
    fp =  popen("ls","r");
    int n_read = fread(ret,1,1024,fp);
    printf("ret = %s\n",ret);
    return 0;

运行结果:
在这里插入图片描述
使用popen函数:

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

int main(void)
{
    char ret[1024] = {"0"};
    FILE *fp;
    fp =  popen("ls","r");
    int n_read = fread(ret,1,1024,fp);
    printf("read ret = %d; ret = %s\n",n_read,ret);
    return 0;
}

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

14.进程总结

进程概念:5问
进程创建fork使用,返回两次
创建进程做了什么事?新的进程回去拷贝(写时拷贝)
程序空间如何分配?(问5)
vfork和fork的区别
exec族函数的使用方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值