Linux高级编程_28_进程

文章目录

进程

概述: 一个正在进行的程序

并行与并发

  • 并行: 执行的程序在不同CPU上同时执行
  • 并发: 一个CPU 多个进程交替执行,因为交替速度很快,所以从宏观上看是同时执行的,但本质就是交替执行。

单道与多道程序

单道程序设计:所有进程一个一个排队执行。若A阻塞,B只能等待,即使CPU处于空闲状态,已弃用

多道程序设计: 在计算机内存中同时存放几道相互独立的 程序 , 它们在管理程序控制之下,相互穿插的运行(并行和并发)

进程控制块(PCB)了解

进程运行时,内核为进程每个进程分配一个PCB(进程控制块),维护进程相关的信息,Linux内核的进程控制块是

task_struct结构体

task_struct结构体

在 /usr/src/linux-headers-xxx/include/linux/sched.h 文件中可以查看task_struct结构体定义

其内部成员有很多,我们掌握以下部分即可:

进程 id: c语言使用pid_t的类型表示, 其实就是一个非负整数

进程的状态:有就绪运行 、挂起、停止等状态。

PCB存储位置

在内核中

进程号:

当前进程的 ID 就是进程号 由系统分配

每一个进程都由一个进程号来标识 其类型为 pid_t 进程号的范围: 0~32767

进程号总是唯一的 但是可以重用 当一个进程结束后,其进程号就可以再次使用了

进程号 为 0 和 1 的进程由内核创建

进程号为 0 的进程通常是 **调度进程,**常被称为交换进程(swapper)。进程号为1 的进程 通常是 init 进程。

除调度进程(0号进程)外,在 linux 下面所有的进程都由进程 init [1]进程直接或者间接创建。

进程号:(PID)

​ 标识进程的一个非负整型数

获取当前进程的进程号 :

​ pid_t getpid( );

​ pid_t 是进程号 是 int 型

进程组号:(PGID)

​ 多个进程在同一组中,该组就是进程组

​ 该组中第一个进程就是组长进程,该组长的进程号就是该进程组的 id

​ 由 进程A 开启的子进程 默认为进程A为同组进程 ,此时A 就是组织进程

获取指定进程的进程组号 :

​ pid_t getpgid ( int pid );

父进程号:(PPID)

​ 由进程A 开启 B 此时进程A 就是B 的父进程

​ 进程B 就是 A 的子进程

获取父进程的进程号 :

​ pid_t getppid();

当进程A 优先 于 进程B 先销毁 此时B就是孤儿进程 (父先挂子存在)此时由1进程充当 父进程

示例:

c 复制代码
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char const *argv[])
{
 //获取当前进程id
 int  pid = getpid();
 cout<<"当前进程号为"<<pid<<endl;
 //获取同组进程id
 int pgid = getpgid(pid);
 cout<<"当前进程组为"<<pgid<<endl;

 //获取父进程id
 int  ppid = getppid();
 cout<<"当前进程父进程id为"<<ppid<<endl;


 //创建子进程
 int xxz_id = fork(); 
 if(xxz_id == 0)
 {
     cout<<"子进程号"<<getpid()<<endl;
     cout<<"子进程的父进程号"<<getppid()<<endl;
     cout<<"当前进程组为"<<getpgid(getpid())<<endl;
     sleep(5);
     exit(0);
 }
 else if (xxz_id >0 )
 {
     wait(NULL);
 }
 return 0;
}

fork函数

作用: 创建进程

语法:

c 复制代码
pid_t fork();
返回值:
 如果在子进程中   返回值就是0,
 如果在父进程中   就是创建的子进程id,
 -1创建失败

fork失败的两个主要原因是:

  • 1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
  • 2)系统内存不足,这时errno的值被设置为ENoM

注意:

1 子进程会从fork函数后开始执行

​ 2 子进程共享父进程所有内容,不包括进程号,父进程号等

markdown 复制代码
	使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。
	地址空间:包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。
	子进程所独有的只有它的进程号,计时器等。

因此,使用fork 函数的代价是很大的。共享相当于 复制了一份

​ 3 谁创建谁回收

示例1

子进程会从fork函数后开始执行

c 复制代码
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char const *argv[])
{
     // 只有父进程执行
     printf("这是一行废话,当前进程 %d\n", getpid());
     // 子进程会从fork函数后开始执行
     int pid = fork();                            // 创建子进程
     printf("hello 进程 当前进程%d\n", getpid()); // 执行两次
     return 0;
}

示例2 :

子进程共享父进程所有内容,不包括进程号,父进程号等

c 复制代码
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char const *argv[])
{
     printf("这是一行废话,当前进程");
     // 子进程共享父进程所有内容,不包括进程号,父进程号等 包括缓冲区
     int pid = fork(); // 创建子进程
     printf("hello 进程 当前进程%d\n", getpid());
     return 0;
}

示例3:

c 复制代码
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int main(int argc, char const *argv[])
{
     char *str = "这是一行废话";
     // 此时输出的内容直接在屏幕上不在缓冲区
     write(1, str, strlen(str));
     int pid = fork();
     printf("hello 进程 当前进程%d\n", getpid());
     return 0;
}

引入多进程创建

也就是对下面这句话的理解

使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。

地址空间:包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。

子进程所独有的只有它的进程号,计时器等。

那这样要是创建多进程呢

多进程创建

解决方案:

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
// 多线程创建 但是没人 回收
int main(int argc, char const *argv[])
{
    for (int i = 0; i < 4; i++)
    {
        //创建子线程
        int pid = fork();
        // 父进程中 pid > 0
        // 子进程中 pid = 0
        if (pid == 0)  
        {
            // 子进程代码
            printf("子进程 %d 的进程号是 %d\n", i, getpid());
            break;
        }
    }
    return 0;
}

此处只是多进程创建 但是 注意 进程 谁创建就要由谁回收 所以 多进程创建就牵扯多进程回收

进程状态

三状态:

就绪态 执行态 等待态


五状态:

新建态、终止态、运行态、 就绪态、阻塞态


注意:

​ 阻塞态在其他语言中进行细分又可以分为阻塞态与休眠态

​ 休眠态又可以分为有限期休眠与无限期休眠

​ 休眠状态下不会抢夺CPU 执行权

进程资源的回收

概述:

因为子进程创建的时候 共享(也就是拷贝)父进程

在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,

这些信息主要主要指进程控制块PCB的信息(包括进程号、退出状态、运行时间等);

回收原则:谁创建谁回收(父进程 回收 子进程资源);

1 wait函数:

作用:回收进程资源

头文件

  • #include <sys/types.h>
  • #include <sys/wait.h>

语法:

c 复制代码
pid_t wait(int *status);      //阻塞

功能:

c 复制代码
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char const *argv[])
{
     // 创建子进程
     int pid = fork();
     if (pid == 0) // 子进程中
     {
           for (int i = 0; i < 5; i++)
           {
               // 休眠一秒
               sleep(1);
               printf("子进程%d 已经休眠了%d秒\n", getpid(), i + 1);
           }
     }
     else if (pid > 0) // 父进程
     {
           // 依据回收原则
           // wait函数回收子进程
           // wait函数用于回收子进程,但是会阻塞当前进程,直到有需要回收子进程为止
           // 如果当前进程中所有的子进程都以被回收,该函数不会阻塞当前进程,会返回-1
           int s = 0;
           int id = wait(&s); // 函数传入的参数是指针 所以是 &
           printf("当前进程%d回收了进程%d\n", getpid(), id);
           printf("状态码是:%d\n", s);
           // 状态码使用的少
     }
     return 0;
}

2 exit 与 _exit()

区别:

  • 1,exit函数属于库函数
  • 2,_exit函数属于系统调用 【效率高】
exit函数
c 复制代码
所需头文件
	#include <stdlib.h>
函数:
	void exit(int status);
参数:
	退出状态,0,正常退出,非0异常退出
_exit函数【*】
markdown 复制代码
所需头文件
	#include <unistd.h>
函数:
	void _exit(int status);
参数:
	退出状态,0,正常退出,非0异常退出
markdown 复制代码
> 注意 退出状态码取值区间   0~255 

示例:

在子进程 计算 10 内数之和,在主进程打印结果

c 复制代码
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

//   > 注意 退出状态码取值区间   0~255
// 在子进程 计算 10 内数之和,在主进程打印结果

// 退出当前进程   库函数exit()   系统调用_exit()
int main(int argc, char const *argv[])
{ 
    int pid = fork(); // 创建子线程
    if (pid == 0)
    {
        int sum = (0 + 10) * 11 / 2;
        sleep(2);
        _exit(sum); // 将sum作为其状态码
    }
    else if (pid > 0) // 父进程
    { 
        int s = 0;
        int pid = wait(&s);    //使用了  wait   回收  进程  但是会阻塞当前进程  一直到 子进程  被回收
        printf("s = %d\n", s);
        printf("退出时状态 %d\n", WIFEXITED(s));
        printf("退出时状态 码 %d\n", WEXITSTATUS(s));
    }
    return 0;
}

3 WIFEXITED(status)与WEXITSTATUS(status)

作用: 从状态码中获取信息

  • 获取进程退出时状态 WIFEXITED(status)
  • 获取进程退出时的状态码 WEXITSTATUS(status)
markdown 复制代码
WIFEXITED:判断进程是否正常退出
	取出子进程的退出信息 WIFEXITED(status)如果子进程是正常终止的,取出的字段值非零。
WEXITSTATUS(status):返回子进程的退出状态值
	退出状态值保存在status变量的8~16 位。

语法:

WIFEXITED(status)

WEXITSTATUS(status)

4 waitpid函数 【*】

markdown 复制代码
 > wait函数用于回收子进程,但是会阻塞当前进程,直到有需要回收子进程为止


 >  waitpid 优势 就是不阻塞主进程 多进程创建的回收

作用: 回收进程

语法:

c 复制代码
头文件
	#include <sys/types.h>
	#include <sys/wait.h>
语法
	pid_t waitpid(pid_t pid, int *status, int options);
功能:
	等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
参数:
	1 pid : 【基本只用到 -1】
			参数 pid 的值有以下几种类型:
		pid > 0 等待进程 ID 等于 pid 的子进程。
		pid = 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
		pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
		pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid的绝对值。
	2 status : 【基本是NULL】
			进程退出时的状态信息。和 wait() 用法一样。
	3 options : 【基本只用 wnohang】
			options 提供了一些额外的选项来控制 waitpid()。
					0:同 wait(),阻塞父进程,等待子进程退出。
					WNOHANG:没有任何已经结束的子进程,则立即返回。(非阻塞)
					WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到)
返回值:
		1)当正常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;
		2)如果设置了选项 WNOHANG,而调用中 waitpid() 还有子进程在运行,且没有子进程退出,返回0, 父进程的所有子进程都已经退出了返回-1; 返回>0表示等到一个子进程退出;(重要)
		3)如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以指示错误所在,
	如:当pid所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid()就会出错返回,这时errno被设置为ECHILD

多进程回收

弥补上面只创建了多进程而没有进行回收的问题

【模板】

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
#include <stdlib.h>

// 多线程创建 并回收 

int main(int argc, char const *argv[])
{
    // 多进程的创建 i就是 几个线程的创建 else if 就增加到i
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int pid = fork();
        if (pid == 0)
        {
            printf("进程%d被创建了\n",getpid());
            break;
        }
    }
    if (i == 0)
    {
        printf("i = %d,当前进程号:%d\n", i, getpid());
    }
    else if (i == 1)
    {
        printf("i = %d,当前进程号:%d\n", i, getpid());
    }
    else if (i == 2)
    {
        printf("i = %d,当前进程号:%d\n", i, getpid());
    }
    else  if (i == 3) // 能走到这 就只能是父进程了
    {
        printf("i = %d,父进程\n",i);
        // 注意使用非阻塞的 waitpid() 要用死循环
        //  waitpid(-1,NULL,WNOHANG) 此函数回收进程时  一参基本就是-1  二参只有不要状态码就是null  基本就用的时三参
        while (1)
        {
            int pid = waitpid(-1, NULL, WNOHANG);
            if (pid == -1)
            {
                printf("所有子进程都已经回收\n");
                break;
            }
            // else if (pid > 0)
            // {
            //     printf("子进程%d被回收\n", pid);
            // }
        }
    }
    return 0;
}

5 atexit函数

作用:进程在退出前可以用 atexit 函数注册退出处理函数

markdown 复制代码
> 注意:
		1,一个进程可以登记多至 32 个函数,这些函数将由exit自动调用。我们称这些函数为终止处理程序,用atexit函数来登记这些函数。
		2, 以登记这些函数的相反顺序调用它们。同一函数如若登记多次,则也被调用多次
		3, 执行注册的函数  还是子进程

语法:

c 复制代码
int atexit (void (*_func) (void))

示例:

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
#include <stdlib.h>
void fun01()
{
    printf("函数1\n,当前进程是%d\n",getpid());
}
void fun02()
{
    printf("函数2\n");
}
void fun03()
{
    printf("函数3\n");
}

int main(int argc, char const *argv[])
{
    int pid = fork();
    if (pid == 0)
    {
        // 注册退出处理函数
        atexit(fun01);
        atexit(fun02);
        atexit(fun03);
        atexit(fun01);
        printf("当前 子 进程%d end\n", getpid());
    }
    else if (pid > 0)
    {
        wait(NULL); // 状态码不要
        printf("当前 父 进程%d end\n", getpid());
    }

    return 0;
}

特殊进程

僵尸进程

markdown 复制代码
> 子进程退出,父进程没有回收子进程资源,子进程为僵尸进程   (有危害)
> 子进程的PID被占用,系统的PID是有数量限制。

孤儿进程

markdown 复制代码
>  父进程先结束,子进程为孤儿进程.                     (无害的)  
>  孤儿进程被1号进程接管(当孤儿进程结束时,1号进程负责回收其资源

守护进程

markdown 复制代码
> 概念:又名精灵进程,是脱离终端的孤儿进程。

进程补充:

1 终端

终端:是与计算机系统相连的一种输入输出设备

markdown 复制代码
> 在UNIx系统中,用户通过终端登录系统后得到一个shell进程,这个终端成为shell进程的控制终端(ControllingTerminal),进程中,控制终端是保存在PCB中的信息,而fork会复制PCB中的信息,因此由Shel1进程启动的其它进程的控制终端也是这个终端。
markdown 复制代码
> 	一个终端同时只能被一个进程池操作
	当shell进程启动a.out后,将终端的控制权交给了a.out,而shell进程失
	去了终端控制权,此时在终端中输入内容,是被a.out接收.
	当a.out进程结束,会将终端控制权交还给shell进程
	如果父进程先与子进程结束,子进程此时不能输入,只能输出

函数:

c 复制代码
作用:
	获取当前进程所属终端名称
			#include <unistd.h>
			char *ttyname(int fd);
	功能:由文件描述符查出对应的文件名
		参数:fd:文件描述符
		返回值:
				成功:终端名
				失败:NULL

示例:证明是同一终端【其实就是底部黑框】

c 复制代码
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>//exit
int main(int argc, char *argv[])
{
    pid_t pid = fork();
    if(pid == 0)//子进程
    {
        sleep(2);
        int num = 0;
        scanf("%d", &num);
        printf("子进程%u中的num=%d,所属终端名:%s\n", getpid(), num, ttyname(0));
    }
    else if(pid > 0)//父进程
    {
        int num = 0;
        scanf("%d", &num);
        printf("父进程%u中的num=%d,所属终端名:%s\n", getpid(), num, ttyname(0));
    }

    return 0;
}

发现 终端名都是一样的 都是 dev/pts/25

2 进程组

概念:

c 复制代码
>	代表一个或多个进程的集合。
	每个进程都有对应的进程组。
	进程组ID为当前进程中第一进程的ID。
	如果一个进程的ID和组ID相同那么这个进程就是组长进程。
	当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。
	shel1进程启动的进程独立为一个进程组
	如果进程中只是组长进程结束,当前进程组不会解散.只有进程组的所有进程离开(终止或转移),该进程组才会解散。
	一个进程可以为自己或子进程设置进程组ID。
		注意:组长进程不能设置进程组id

			如果进程 ID==进程组 ID==会话ID,那么该进程为会话首进程(会长)。

示例:

c 复制代码
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    int xxz_pid = fork(); // 创建子进程
    if (xxz_pid == 0)
    {    
        printf("当前子进程id:%d\t所属进程组id:%d\n",getpid(),getpgrp());//getpgrp():获取当前进程所在的进程组id
    }
    else if ( xxz_pid > 0)
    {
       printf("当前父进程id:%d\t所属进程组id:%d\n",getpid(),getpgrp());
    }
    return 0;
}


设置当前进程或子进程所在的进程组

c 复制代码
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    int xxz_pid = fork(); // 创建子进程
    if (xxz_pid == 0)
    {   
        //设置当前进程或子进程所在的进程组
        setpgid(getpid(),getpid()); 
        printf("当前子进程id:%d\t所属进程组id:%d\n",getpid(),getpgrp());  //getpgrp():获取当前进程所在的进程组id
    }
    else if ( xxz_pid > 0)
    {
       printf("当前父进程id:%d\t所属进程组id:%d\n",getpid(),getpgrp());
    }
    return 0;
}

3 会话

概念:

c 复制代码
>	
	会话是一个或多个进程组的集合。
	一个会话可以有一个控制终端。这通常是终端设备或伪终端设备;建立与控制终端连接的会话首进程被称为控制进程;
	一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组;
	如果一个会话有一个控制终端,则它有一个前台进程组,其它进程组为后台进程组;
	如果终端接口检测到断开连接,则将挂断信号发送至控制进程(会话首进程)

函数getsid

c 复制代码
> 作用:获取会话id
	所需头文件
			#include <unistd.h>
	函数
			pid_t getsid(pid_t pid);
	参数:
			pid:进程号,pid 为 0 表示查看当前进程 session ID(会话id)
	返回值:
			成功:返回调用进程的会话 ID
			失败:-1
组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。

函数setsid

c 复制代码
>
	作用:创建会话
				所需头文件
						#include <unistd.h>
				函数:
					pid_t setsid(void);
				功能:
					创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID。调用了setsid 函数的进程,既是新的会长,也是新的组长。
				参数:
						无
				返回值:
						成功:返回调用进程的会话 ID
						失败:-1
注意实现:
1,组长进程不能设置为会话
2,需有root权限(ubuntu 不需要)
3,新会话丢弃原有的控制终端,该会话没有控制终端

示例1:

c 复制代码
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
//     会话
int main(int argc, char const *argv[])
{
     int xxz_pid = fork();
     if (xxz_pid == 0)
     {
           setpgid(getpid(), getpid());
           printf("当前子进程id:%d\t所属进程组id:%d\t所属会话id:%d\n", getpid(), getpgrp(), getsid(getpid()));
     }
     else if (xxz_pid > 0)
     {
           printf("当前父进程id:%d\t所属进程组id:%d\t所属会话id:%d\n", getpid(), getpgrp(), getsid(getpid()));
     }
     return 0;
}


示例2: 创建新会话

注意 组长id 不能设置为会话

c 复制代码
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
//     创建新会话  
int main(int argc, char const *argv[])
{
 int xxz_pid = fork();
 if (xxz_pid == 0)
 {
     // 设置当前进程或子进程所在的进程组
     // setpgid(getpid(), getpid());
     // 设置当前进程为一个新的会话
     // 组长进程不能设为新的会话
     setsid();
     printf("当前子进程id:%d\t所属进程组id:%d\t所属会话id:%d\n", getpid(), getpgrp(), getsid(getpid()));
 }
 else if (xxz_pid > 0)
 {
     printf("当前父进程id:%d\t所属进程组id:%d\t所属会话id:%d\n", getpid(), getpgrp(), getsid(getpid()));
 }
 return 0;
}

4 守护进程

  • 1 忽略SIGHUP(信号)防止被终端误杀
  • 2 创建子进程,父进程退出(必须) 所有工作在子进程中进行形式上脱离了控制终端
  • 3 在子进程中创建新会话(必须) setsid()函数使子进程完全独立出来,脱离控制
  • 4 改变当前目录为根目录(不是必须) chdir()函数 防止占用可卸载的文件系统 也可以换成其它路径
  • 5 重设文件权限掩码(不是必须)umask()函数 防止继承的文件创建屏蔽字拒绝某些权限 增加守护进程灵活性
  • 6 关闭文件描述符(不是必须) 继承的打开文件不会用到,浪费系统资源,无法卸载
  • 7 开始执行守护进程核心工作(必须) 守护进程退出处理程序模型

vfork

函数:

c 复制代码
pid_t vfork(void);

作用:

markdown 复制代码
vfork 函数和fork 函数一样都是在已有的进程中创建一个新的进程,但它们创建的子进程是有区别的

vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行。

vfork 和fork一样都创建一个子进程,但它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用

exec(或exit),于是也就不访问该地址空间。

exec函数簇 【*】

建议:

  • 调用系统的 使用 execlp ()
  • 调用别人的 用 execl ()

语法:

c 复制代码
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[]);

作用:

markdown 复制代码
如果想要通过运行的进程,启动另一个程序,需要用到exec函数

exec 后字母的含义

c 复制代码
>	l:表示exe函数族的参数是通过列表(list),传递。
	v:表示exe函数族的参数是通过指针数组(vector),传递。
	p:p表示通过环境变量查找命令(程序)
	e:可以使用系统环境变量。
注意:
	exec 函数族的 最后一个 参数为NULL,

示例:

c 复制代码
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    printf("执行exec前\n");
    // 此时我们发现,该函数后的代码没有被执行
    // 因为exec启动了别人的程序,顾执行的是启动的程序中的代码
    // 将不在执行当前进程中的代码
    // execl("/usr/games/sl","./sl",NULL);
    // execl("/bin/ls","./ls","-l","-a",NULL);
    //  execl("/bin/ls","./ls","-la",NULL);
    // p:在环境变量path中寻找
    //  execlp("ls","./ls","-la",NULL);
    // 此时t.out不在环境变量中,顾找不到
    //  execlp("t.out","./t.out",NULL);
    //  execl("./t.out","./t.out",NULL);
    char *tem[] = {"./ls", "-l", "-a", NULL};
    execv("/bin/ls", tem);
    printf("执行exec后\n");
    return 0;
}

exec函数族与vfork结合

优势:

  • 1,节省内存
  • 2,可以与父进程交替执行

示例1

c 复制代码
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    for (int i = 0; i < 3; i++)
    {
        sleep(1);
        printf("test i = %d\n", i);
    }
    return 0;
}

目的:

将03_test.c编写生成可执行文件 t

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc, char const *argv[])
{
	int pid = vfork();
	if(pid == 0)
	{
		execl("./t","./t",NULL);
	}
	else if(pid > 0)
	{
		for (int i = 0; i < 3; i++)
		{
			sleep(1);
			printf("code i = %d\n",i);
		}
			wait(NULL);
	}
	return 0;
}

system

作用

system 会调用 fork 函数产生子进程,子进程调用 exec 启动要启动的其他程序

语法

c 复制代码
	#include <stdlib.h>
		int system(const char *command);
参数:
	要执行的命令的字符串。
返回值:
	如果command 为 NULL,则system()函数返回非 O,一般为 1。
	如果 system()在调用/bin/sh 时失败则返回127,其它失败原因返回-1。

示例:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc, char const *argv[])
{
	printf("system执行前\n");
	int code = system("./my.sh");
	printf("%d\n",WIFEXITED(code));
	printf("system执行后\n");
	return 0;
}	
相关推荐
PyAIGCMaster21 分钟前
ubuntu装P104驱动
linux·运维·ubuntu
奈何不吃鱼21 分钟前
【Linux】ubuntu依赖安装的各种问题汇总
linux·运维·服务器
icy、泡芙23 分钟前
T527-----音频调试
linux·驱动开发·音视频
aherhuo26 分钟前
kubevirt网络
linux·云原生·容器·kubernetes
zzzhpzhpzzz35 分钟前
Ubuntu如何查看硬件型号
linux·运维·ubuntu
蜜獾云37 分钟前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
o(╥﹏╥)1 小时前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
娶不到胡一菲的汪大东1 小时前
Ubuntu概述
linux·运维·ubuntu
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
那就举个栗子!2 小时前
Ubuntu 20.04下Kinect2驱动环境配置与测试【稳定无坑版】
linux·ubuntu