“从创建到管理,Linux进程编程是你掌握系统资源的金钥匙!“#Linux系统编程之进程【上】

前言

  本篇博文将带您踏上Linux系统编程的旅程,聚焦进程管理的精髓(上篇)。从进程的基本概念出发,我们深入探索fork函数的神奇之处,揭示其如何创造新进程及其背后的复杂机制。了解进程创建的每一个细节,包括内存空间的复制、文件描述符的继承等,让您对Linux的并发处理能力有更深的认知。随后,我们将fork函数的实际应用场景娓娓道来,并总结其使用要点。此外,我们还特别介绍了vfork这一特殊的进程创建方式,让您对Linux的进程创建机制有更全面的掌握。最后,我们讨论进程的优雅退出,确保您能够正确处理进程生命周期的结束阶段。无论您是Linux系统编程的新手还是寻求进阶的开发者,这篇博文都将为您提供宝贵的知识和见解。期待您的点赞与关注,一同探索Linux系统编程的无限可能!

预备知识

  一、C变量
  二、基本输入输出
  三、流程控制
  四、函数
  五、指针
  六、字符串
  七、Linux系统基本操作命令如mkdir,ls -l等。
  八、计算内存知识:堆,栈等

  如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!

一、 进程相关概念

1.1 什么是程序,什么是进程,右什么区别?

  程序是静态的概念,gcc xxx.c –o pro
  磁盘中生成pro文件,叫做程序
  进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程

1.2 如何查看系统中有那些进程?

  使用ps指令查看,实际工作中,配合grep来查找程序中是否存在某一个进程

ps -aux|grep init

  命令运行结果

CLC@Embed_Learn:~$ ps -aux|grep init
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
root         1  0.0  0.1  24704  2596 ?        Ss   Aug05   0:01 /sbin/init
CLC      41147  0.0  0.0  13592   960 pts/0    S+   23:05   0:00 grep --color=auto init

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

top

  命令运行结果

Tasks: 331 total,   1 running, 330 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.7%us,  1.4%sy,  0.0%ni, 97.8%id,  0.0%wa,  0.1%hi,  0.0%si,  0.0%st
Mem:   2189824k total,  1986184k used,   203640k free,   128396k buffers
Swap:  5998588k total,      160k used,  5998428k free,   860572k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                 
 2460 CLC        9 -11  418m 5984 4180 S    3  0.3 334:19.61 pulseaudio                                              
 1281 root      20   0  449m 274m  11m S    2 12.8  55:03.10 Xorg                                                    
31296 CLC       20   0  610m  24m  13m S    1  1.2   2:26.43 gnome-terminal                                          
41149 CLC       20   0 17468 1500  980 R    1  0.1   0:00.07 top                                                     
 2448 CLC       20   0 1371m 112m  38m S    0  5.3  52:35.04 compiz                                                  
    1 root      20   0 24704 2596 1344 S    0  0.1   0:01.49 init                                                    
    2 root      20   0     0    0    0 S    0  0.0   0:00.08 kthreadd                                                
    3 root      20   0     0    0    0 S    0  0.0   0:01.55 ksoftirqd/0                                             
    5 root       0 -20     0    0    0 S    0  0.0   0:00.00 kworker/0:0H                                            
    7 root      20   0     0    0    0 S    0  0.0   1:55.71 rcu_sched                                               
    8 root      20   0     0    0    0 S    0  0.0   1:44.82 rcuos/0                                                 
    9 root      20   0     0    0    0 S    0  0.0   1:03.66 rcuos/1                                                 
   10 root      20   0     0    0    0 S    0  0.0   0:58.21 rcuos/2                                                 
   11 root      20   0     0    0    0 S    0  0.0   0:48.37 rcuos/3                                                 
   12 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/4                                                 
   13 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/5                                                 
   14 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/6                                                 
   15 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/7                                                 
   16 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/8                                                 
   17 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/9                                                 
   18 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/10                                                
   19 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/11                                                
   20 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/12                                                
   21 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/13                                                
   22 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/14                                                
   23 root      20   0     0    0    0 S    0  0.0   0:00.00 rcuos/15

  注意:这个结果会实时改变的,和Windows是一样的,都会刷新。

1.3 什么是进程标识符

  每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证

Pid=0:  称为交换进程(swapper)
作用—进程调度
Pid=1:init进程
作用—系统初始化

  编程调用getpid函数获取自身的进程标识符,getppid获取父进程的进程标识符
  程序代码

#include <stdio.h>
使用getpid函数必须包含以下头文件
#include <sys/types.h>
#include <unistd.h>

int main()
{
        pid_t pid;
        //pid_t getpid(void);
        pid = getpid();            调用getpid函数获取当前进程标识符    
        printf("pid = %d\n",pid);  输出进程标识符
        while(1);

        return 0;
}

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

1.4 什么叫父进程,什么叫子进程?

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

1.5 C程序的存储空间是如何分配?

请添加图片描述
在这里插入图片描述

二、 创建进程函数fork的使用

2.1 fork函数介绍

2.1.1 函数功能

  创建一个新的子进程。
  子进程是父进程的几乎完全相同的副本,包括代码、数据、堆、栈、文件描述符等。
  父子进程从fork函数的返回点开始独立执行。

2.1.2 返回值

  fork函数的一个独特之处在于它只被调用一次,但会返回两次:

  在父进程中,fork返回新创建的子进程的进程ID(PID)。
  在子进程中,fork返回0。
  如果出现错误(如系统资源不足),fork返回-1,并设置errno以指示错误类型。

2.1.3 进程间关系

  子进程是父进程的副本,但它们各自拥有独立的内存空间和资源管理。
  父子进程之间通过共享某些资源(如文件描述符)进行通信和协作。

2.2 fork函数编程实战

2.2.1 单独调用fork和多使用一次getpid

  程序代码

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

int main()
{
        pid_t pid;
        //pid_t getpid(void);
        pid = getpid();                                 获取进程标识符

        //pid_t fork(void);
        fork();                                         创建新进程

        printf("pid1 = %d\n pid2 = %d\n",pid,getpid()); 输出第一次和第二次获取到的进程标识符


        return 0;
}

  程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ gcc Process_programming.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ ./a.out 
pid1 = 41609
 pid2 = 41609
pid1 = 41609
 pid2 = 41610

  从输出结果可以观察到,printf函数实际上被调用了多次,但其中关键的是,它首次被调用时仅代表父进程的上下文,输出了父进程的标识符。随后,在fork函数调用之后,由于fork的特性,它会在父进程中返回子进程的PID(进程标识符),而在新创建的子进程中返回0。因此,在达到fork函数的返回点之后,printf的第二次调用分别在父进程和子进程中独立执行,导致在控制台上既输出了父进程的标识符,也输出了子进程的标识符(在子进程中,由于fork返回0,通常会通过特定的逻辑处理来区分并输出相应的信息)。这样,就清晰地展示了父子进程从fork函数的返回点开始独立执行的行为。

2.2.2 通过getpid的返回值判断父子进程

  程序代码

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

int main()
{
        pid_t pid;
        //pid_t getpid(void);
        pid = getpid();

        //pid_t fork(void);
        fork();
        if(pid == getpid())			判断父进程
        {
                printf("This is the parent process,and its PID is %d\n",getpid());
        }
        else                        判断子进程
        {
                printf("This is a child process,and the  child process is %d\n",getpid());
        }


        return 0;
}

  程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ gcc Process_programming2.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ ./a.out 
This is the parent process,and its PID is 41710
This is a child process,and the  child process is 41711
2.2.3 通过fork函数返回值判断父子进程

  fork函数调用成功,返回两次;返回值为0,代表当前进程是子进程;返回值非负数,代表当前进程为父进程。
  程序代码

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

int main()
{
        pid_t pid;
        pid_t fk;
        //pid_t getpid(void);


        //pid_t fork(void);
        fk = fork();
        if(fk > 0)
        {
                printf("This is the parent process,and its PID is %d\n",getpid());
        }
        else if(fk == 0)
        {
                printf("This is a child process,and the  child process is %d\n",getpid());
        }


        return 0;
}

  程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ gcc Process_programming3.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ ./a.out 
This is the parent process,and its PID is 41747
This is a child process,and the  child process is 41748
2.2.4 fork返回值补充

  程序代码

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

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

        //pid_t fork(void);
        retfk = fork();
        pid2 = getpid();
        printf("after fork: pid = %d\n",pid2);

        if(pid == pid2)
        {
                printf("This is the parent process: retfk =  %d\n",retfk);
        }
        else
        {
                printf("This is a child process,and the  child process is %d retfk = %d\n",getpid(),retfk);
        }


        return 0;
}

  程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ gcc Process_programming4.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/02_Process_creat$ ./a.out 
before fork: pid = 41912
after fork: pid = 41912
This is the parent process: retfk =  41913
after fork: pid = 41913
This is a child process,and the  child process is 41913 retfk = 0

  通过fork()系统调用,程序成功地在父进程中创建了一个新的子进程。随后,父进程和子进程都继续执行了fork()调用之后的代码。在父进程中,fork()返回了子进程的PID,而子进程中fork()返回了0,分别用于区分父进程和子进程的身份。通过getpid()的调用,父进程和子进程各自打印了它们的PID,以及fork()的返回值,清晰地展示了进程创建的过程和父子进程间的关系。

三、 进程创建发生了什么事

3.1 新进程内存分配

请添加图片描述

3.2 编程验证内存空间独立

3.2.1 程序代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        pid_t pid;
        pid_t fk;
        int data = 10;        初始化data的值为10
        //pid_t getpid(void);

        printf("father : id = %d\n",getpid());
        //pid_t fork(void);
        fk = fork();
        if(fk > 0)
        {
                printf("This is the parent process,and its PID is %d\n",getpid());
        }
        else if(fk == 0)
        {
                printf("This is a child process,and the  child process is %d\n",getpid());
                data = data + 100;      data在子进程中加100
        }
        printf("data = %d\n",data);     父子进程分别输出data的值


        return 0;
}
3.2.2 程序运行结果
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/03_New_process_memory_allocation$ gcc Process_programming.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/03_New_process_memory_allocation$ ./a.out 
father : id = 42213
This is the parent process,and its PID is 42213
data = 10
This is a child process,and the  child process is 42214
data = 110

  运行结果表明,data只在子进程中加100,在父进程中没有做任何改变。数码父子进程间的内存空间是独立的。

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

4.1 创建新进程的实际应用场景

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

4.1.1 上图(1)的解释

  如下图
请添加图片描述

4.1.2 对上图(1)编程实战

  程序代码

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

int main()
{
        pid_t pid;
        pid_t fk;
        int data;
        //pid_t getpid(void);
printf("father : id = %d\n",getpid());
        //pid_t fork(void);
        while(1)
        {

                puts("Please enter a request");
                scanf("%d",&data);      
                if(data == 1)			data=1时客户端接入
                {
                        fk = fork();    创建子进程
                        if(fk > 0)      主进程什么都不做
                        {

                        }
                        else if(fk == 0) 子进程进行连接并输出自己的pid
                        {
                                while(1)
                                {
                                        printf("This is a child process,and the  child process is %d\n",getpid());
                                        sleep(3); 睡眠3秒防止刷屏
                                }
                        }
                }
                else                    data != 1就等待接入
                {
                        puts("Wait for input!");
                }


        }


        return 0;
}

  程序运行结果

  如下图:

请添加图片描述

4.2 fork函数总结

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

五、 vfork创建进程

5.1 vfork和fork的区别

  关键区别一:
  vfork 直接使用父进程存储空间,不拷贝。

  关键区别二:
  vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。

5.2 使用fork观察进程调度情况

5.2.1 程序代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        pid_t fk;
        //pid_t getpid(void);

        //pid_t fork(void);
        fk = fork();
        if(fk > 0)
        {
                while(1)	一直重复发出父进程的进程标识符
                {
                        printf("This is the parent process,and its PID is %d\n",getpid());
                        sleep(1);
                }
        }
        else if(fk == 0)
        {
                while(1)	一值重复发出子进程的进程标识符
                {
                        printf("This is a child process,and the  child process is %d\n",getpid());
                        sleep(1);
                }
        }



        return 0;
}
5.2.2 程序运行结果
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/05_Vfork_creat_process$ gcc Process_programming.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/05_Vfork_creat_process$ ./a.out 
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
This is a child process,and the  child process is 45608
This is the parent process,and its PID is 45607
^C

  从以上运行结果可以看出,程序在执行过程中,父进程(PID为45607)和子进程(PID为45608)都在不断执行并打印出各自的进程ID和角色信息。这表明fork()调用在父进程中成功创建了一个子进程,并且父进程和子进程都进入了自己的执行路径,不断地打印出各自的信息。也表明了父子进程各自空间独立,互不干扰。

5.3 使用vfork观察进程调度情况

5.3.1 程序代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        pid_t fk;
        //pid_t getpid(void);

        //pid_t fork(void);
        fk = vfork();        使用vfork创建子进程,返回值与fork一致
        if(fk > 0)
        {
                while(1)
                {
                        printf("This is the parent process,and its PID is %d\n",getpid());
                        sleep(1);
                }
        }
        else if(fk == 0)
        {
                while(1)
                {
                        printf("This is a child process,and the  child process is %d\n",getpid());
                        sleep(1);
                }
        }



        return 0;
}
5.3.2 程序运行结果
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/05_Vfork_creat_process$ gcc Process_programming3.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/05_Vfork_creat_process$ ./a.out 
This is a child process,and the  child process is 45705
This is a child process,and the  child process is 45705
This is a child process,and the  child process is 45705
This is the parent process,and its PID is 45704
This is the parent process,and its PID is 45704
This is the parent process,and its PID is 45704
This is the parent process,and its PID is 45704
This is the parent process,and its PID is 45704
^C

  从以上运行结果可以看出,程序在执行过程中,父进程(PID为45704)和子进程(PID为45705)。且子进程执行3次后退出才执行的父进程。因为子进程直接使用父进程存储空间,不拷贝,必须保证子进程先运行,当子进程调用exit退出后,父进程才执行。

5.4 查看控制子进程变量cnt的值验证子进程使用父进程空间

5.4.1 程序代码
#include <unistd.h>
#include <stdlib.h>

int main()
{
        pid_t fk;
        //pid_t getpid(void);
        int cnt = 0;
        //pid_t fork(void);
        fk = vfork();
        if(fk > 0)
        {
                while(1)
                {
                        printf("This is the parent process,and its PID is %d\n",getpid());
                        sleep(1);
                        printf("cnt == %d\n",cnt);    输出cnt的值
                }
        }
        else if(fk == 0)
        {
                while(1)
                {
                        printf("This is a child process,and the  child process is %d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3)
                        {
                                exit(0);            退出子进程
                        }
                }
        }

        return 0;
}
5.4.2 程序运行结果
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/05_Vfork_creat_process$ gcc Process_programming4.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_process_programming/05_Vfork_creat_process$ ./a.out 
This is a child process,and the  child process is 45782
This is a child process,and the  child process is 45782
This is a child process,and the  child process is 45782
This is the parent process,and its PID is 45781
cnt == 3
This is the parent process,and its PID is 45781
cnt == 3
This is the parent process,and its PID is 45781
cnt == 3
This is the parent process,and its PID is 45781
cnt == 3
This is the parent process,and its PID is 45781
cnt == 3
This is the parent process,and its PID is 45781
^C

  从以上运行结果可以看出,程序中的自进程是直接使用父进程的内存空间。

六、进程退出

6.1 进程退出的方式

  如下图
请添加图片描述

6.2 进程退出补充

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

结束语

  非常感谢您的耐心阅读!在您即将离开之际,如果您觉得内容有所收获或启发,不妨动动手指,点个赞再走,这将是对我莫大的鼓励和支持。衷心感谢您的点赞与关注,期待未来能继续为您带来更多有价值的内容!谢谢您!

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值