Linux高级编程——进程

进程、线程

进程就是正在运行的程序,或程序的一次执行过程。

通过同一程序可以产生多个进程(即所谓的多开)。

操作系统(Operating System,简称OS)会给每个进程分配一个不同的整数编号(大于或等于1),这个编号就是所谓的PID(Process Identification,进程 ID),操作系统就是通过 PID区分和管理不同的进程。

进程是操作系统分配资源(PID、私有地址空间)的基本单位。一旦进程结束后,操作系统会自动回收进程所有的资源。

系统中的所有进程可以并发或并行执行。

特点:多任务并发,高并发。

多进程技术可以实现多任务并发处理,但系统中的进程并不是越多越好,并且同时执行的进程数是有上限的。如果进程数太多,会消耗系统很多资源,导致系统性能急剧下降甚至无法正常运行,新进程无法继续创建。

父进程与子进程

Bootloader 程序:引导加载程序

  • 程序(Program):一个可执行的文件。

  • 软件(Software):程序+文档。

  • 硬件(Hardware):

  • fork,vfork,exec,system,popen

  • getgid获得本进程ID,getppid获得父进程ID

  • exit结束当前进程,_exit

  • waitpid,wait

fork系统调用

fork:创建一个新的子进程,新的子进程与父进程完全一样,直接拷贝了父进程的地址空间。创建完成后,父进程和子进程将会同时运行(并发运行)。“一次调用,两次返回”,但它在父进程和子进程中的返回值是不一样的,子进程返回0,父进程返回子进程PID。

通过PID查进程的名称

ps -e | grep 进程PID 
  • 孤儿进程:调用 fork 后,父进程先结束,子进程就成为了孤儿进程,孤儿进程会被系统进程(init/systemd)自动收养,成为其子进程。

  • 僵尸进程(Zombie Process):调用 fork 后,子进程先结束了,但父进程没有为其“收尸”,子进程就变成成了僵尸进程虽然僵尸进程结束了,但其PID、推出状态信息等资源还没有被系统释放(内存空间会被释放),直到父进程“收尸”处理或父进程结束。僵尸进程会白白浪费系统资源,如果数量很多,会严重影响系统性能,所以我们要避免僵尸进程出现。

fork 函数效率很高,内部采用“写时拷贝”机制,与父进程共享空间,如果不改变变量值,就一直共享内存空间。

wait函数

#include <sys/types.h>

#include <wait.h>

int wait(int *status)

wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下的返回值为子进程的PID。

用法一:wait(NULL)用以杀死僵尸进程

​ 成功会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

用法二:wait(int *status)

​ 参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中, 这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪一个信号结束的等信息。

1,WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。

(请注意,虽然名字一样,这里的参数status并不同于wait唯一的参数–指向整数的指针status,而是那个指针所指向的整数,切记不要搞混了。)

2, WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说, WIFEXITED返回0,这个值就毫无意义。

https://blog.csdn.net/wyhh_0101/article/details/83933308

vfork系统调用

vfork:创建一个新的子进程,返回值与fork一样,不一样的是vfork创建子进程后,子进程先运行,父进程会一直阻塞(Blocked),直到子进程结束(调用 _exit 函数)或调用了exec 系列函数后,父进程才会被调度运行。子进程并没有拷贝父进程的地址空间,而是与其共享。

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

int g = 1;
int main()
{
    int i = 1;
    pid_t pid = vfork();//改成fork不会等待子进程
    if(pid < 0) perror("vfork");
    if(pid == 0)
    {
        g++;
        i++;
        printf("我是子进程!(g = %d, i = %d)\n",g, i);
        sleep(3);//延时三秒
        _exit(0);//结束进程,子进程与父进程共用一片缓冲区
    }
    else
    {
        printf("我是父进程!(g = %d, i = %d)\n",g, i);
    }
        return 0;
}

运行结果:
我是子进程!(g = 2, i = 2)
我是父进程!(g = 2, i = 2)

若为fork运行结果:
我是父进程!(g = 1, i = 1)
我是子进程!(g = 2, i = 2
exec 函数

exec 系列函数会加载新程序的指令和数据到进程地址空间通常和 fork 和 vfork 函数配合使用(在子进程中调用 exec),调用时,直接清空当前进程,执行命令内的程序,如果需要父进程等待子进程可以调用wait(NULL)或者wait(pid,)

system()函数内部就是fork与execl的结合

_exit 函数:Linux 系统调用,结束当前进程。

exit 函数:标准 C 库函数,在Linux 系统上,该函数内部首先会逆序调用所有已经注册好的退出函数,然后清空 I/O 缓冲区,最后再调用 _exit 函数。

atexit(fun1):注册一个函数,在程序退出(exit)前调用一次fun1函数,并且越先注册的函数越慢执行(栈属性)

回调函数(Callback Function):定义之后并没有显式调用,而是在发生某个事件或满足某个条件时会被自动调用的函数。

system:标准 C 库函数,执行一个shell命令,在命令完成之后才能返回。

//内部实现
int system(const char *command)
{
	pid_t pid = fork();//产生一个进程
	if(pid == 0)
	{
		return 1;
	}
	else
    {
        return execl("/bin/sh","sh","-c",command,(char *)NULL);//执行command命令
    }
    int status;
    waitpid(pid, &status, 0);//等待进程结束
    
    return status;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值