IO【4】(进程)

260 阅读9分钟

​ “这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

进程

  • 任务:目标结果
  • 程序:是为了完成任务,编写的一段代码,是一个静态的。

进程的概念

进程是程序为了完成任务执行的一次过程,是一个动态的(动态创建,调度,执行和消亡的整个过程)。进程被称为**资源分配的最小单位,**因为每一个进程在启动初期,都会申请一个0-4G的虚拟空间。

这个空间分为两个部分,0-3G用户空间,3-4G是内核空间,0-3G是进程之间独有的空间。互不影响。3-4G属于多进程共享的空间(后续用于进程之间通讯使用),因为进程用户空间相互独立,互不影响所以安全性较高。还会申请一个PCB进程控制块 是一个结构体。task_struct 里面存储了所有进程的资源

如:PC程序计数器、堆栈、文件描述符、进程的状态 、进程号等等

操作系统启动时会自动创建3个进程:

  • 0:负责引导系统启动,也会创建一个1号进程--》init进程
  • 1:负责初始化硬件**,回收资源**
  • 2:负责资源的分配,系统的调度

进程之间存在这个一种竞态。执行速度是不一定的,所以父子进程结束的快慢也是不一定。这里考虑单核和多核cpu吧。

进程的调度机制

时间片轮询,上下文切换

进程的状态

进程状态.png

进程的标志

进程号(PID):linux分配的进程的编号,每个进程都不一样方便管理。

进程在结束时,会释放进程号的所有权。其他进程等待它释放一段时间后

分配,并不会结束后立马去分配出去。

进程相关的命令

1.pstree--以树形显示所有进程

如果加上 -p 参数会显示进程号

linux@ubuntu:~/test/test04$ pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ├─NetworkManager─┬─dhclient
        │                └─2*[{NetworkManager}]
        ├─VGAuthService
        ├─accounts-daemon───2*[{accounts-daemon}]

linux@ubuntu:~/test/test04$ pstree -p
systemd(1)─┬─ModemManager(804)─┬─{ModemManager}(846)
           │                   └─{ModemManager}(854)
           ├─NetworkManager(852)─┬─dhclient(958)
           │                     ├─{NetworkManager}(912)
           │                     └─{NetworkManager}(916)
           ├─VGAuthService(1283)
           ├─accounts-daemon(812)─┬─{accounts-daemon}(826)
           │                      └─{accounts-daemon}(830)         

2.ps -ef--主要查看父子进程关系

linux@ubuntu:~/test/test04$ ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
linux      2214   1581  0 09:07 ?        00:00:50 /usr/lib/gnome-terminal/
linux      2220   2214  0 09:07 pts/0    00:00:00 bash
linux      2317   2214  0 09:28 pts/1    00:00:00 bash
linux      2332   2317  0 09:30 pts/1    00:00:00 vi test.c

PID 进程ID PPID 父进程ID

3.ps aux--主要查看进程的状态

linux@ubuntu:~/test/test04$ ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.2 159996  9328 ?        Ss   09:01   0:04 /sbin/in
root          2  0.0  0.0      0     0 ?        S    09:01   0:00 [kthread
root          3  0.0  0.0      0     0 ?        I<   09:01   0:00 [rcu_gp]

进程的状态:

R运行态
S休眠态
I(i的大写)空闲态
T停止态
Z僵尸态
<优先级高进程
N优先级低
l(L的小写)该进程中包含线程
+前台进程
s会话首进程

4.ps -ajx--主要查看家族关系

linux@ubuntu:~/test/test04$ ps -ajx
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
     0      1      1      1 ?            -1 Ss       0   0:04 /sbin/init a
     0      2      0      0 ?            -1 S        0   0:00 [kthreadd]
.
.
.
  2214   2220   2220   2220 pts/0      3812 Ss    1000   0:00 bash
  2214   2317   2317   2317 pts/1      2332 Ss    1000   0:00 bash
  2317   2332   2332   2317 pts/1      2332 S+    1000   0:00 vi test.c
     2   3637      0      0 ?            -1 I        0   0:11 [kworker/0:1
     2   3666      0      0 ?            -1 I        0   0:00 [kworker/u25
     2   3740      0      0 ?            -1 I        0   0:00 [kworker/u25
     2   3808      0      0 ?            -1 I        0   0:00 [kworker/u25
  2220   3812   3812   2220 pts/0      3812 R+    1000   0:00 ps -ajx

PGID:进程组ID

SID :会话ID

会话

会话是为了完成一个任务,一个或多个进程组成的一个集合。会话中有三大部分

  1. 会话首进程:创建会话的进程
  2. 前台进程组:一个具有亲缘关系的前台进程的集合
  3. 多个后台进程组:可以不具备亲缘关系的多个进程的集合

📢注意:会话首进程管理整个会话,如果会话首进程结束,那么会话中的所有进程都会结束

5.top--CPU占有率

动态查看进程信息:主要查看进程cpu占有率

linux@ubuntu:~/test/test04$ top

top - 19:27:56 up 10:26,  1 user,  load average: 0.02, 0.03, 0.00
任务: 327 total,   1 running, 258 sleeping,   0 stopped,   0 zombie
%Cpu(s):  4.5 us,  3.1 sy,  0.0 ni, 92.5 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  4001668 total,  1796372 free,  1312536 used,   892760 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  2441892 avail Mem 

进程 USER      PR  NI    VIRT    RES    SHR  %CPU %MEM     TIME+ COMMAND       
  1601 linux     20   0  393132  56328  32848 S  3.0  1.4   1:05.81 Xorg          
  1726 linux     20   0 3056316 205076  95484 S  3.0  5.1   1:37.78 gnome-shell   
  2214 linux     20   0  796560  57740  43144 S  2.0  1.4   0:54.16 gnome-termin+ 
  3822 linux     20   0   51364   4156   3380 R  1.0  0.1   0:00.31 top           
  1204 root      20   0  194612  13644  11120 S  0.3  0.3   0:56.93 vmtoolsd 

6.jobs--查看用户后台进程列表

  1. ctrl + z :会将前台运行的进程暂停保存到后台
  2. fg :会将后台暂停的进程恢复到前台去运行
  3. fg + %序列号 :将指定的后台暂停程序恢复到前台运行
  4. bg :会将后台暂停程序在后台运行
  5. bg + %序列号 :指定哪一个进程
  6. 可执行程序名 + & :将进程运行在后台

创建进程:fork

  1. 头文件:#include <sys/types.h> #include <unistd.h>

  2. 原型: pid_t fork(void);

  3. 功能:创建一个子进程

  4. 参数:无

  5. 返回值:

    ​ 成功:📌返回给父进程子进程的ID号,返回给子进程 0

    ​ 失败返回-1;

    ​ 📢fork:失败的条件只有一个,内存不够了。

  6. 代码:

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

int main(int argc, const char *argv[])
{
    int fd = open("./1.txt",O_RDWR|O_CREAT,0666);
    if (-1 ==fd)
    {
        perror("open");
        return -1;
    }
    //创建一个子进程
    pid_t pid  = fork();//创建了一个子进程
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    //利用返回值的机制,来让父子进程分别干不同的事情
    if(pid == 0)
    {
        //子进程
        printf("我是子进程\n");
        write(fd,"hello world",11);
    }
    else if(pid > 0){
        //父进程
        char buf[123] = {0};
        sleep(1);
        close(fd);
        fd = open("./1.txt",O_RDONLY);
        printf("我是父进程,我的子进程的ID号为%d\n",pid);
        read(fd,buf,sizeof(buf));
        printf("buf = %s\n",buf);
    }

    while(1);

    return 0;
}
linux@ubuntu:~/test/test04$ ./a.out 
我是子进程
我是父进程,我的子进程的ID号为3866
buf = hello world!

闹心的fork!

这里要注意第二个的linux@ubuntu:~/test/test04$,linux机制规定了一点,作为子进程,必须让父进程帮它回收。父进程在运行时,bash解析器要比父进程晚一点,因为bash解析器是父进程的父进程,要等待父进程结束,回收父进程。

1.坑
 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 int main(int argc, const char *argv[])
 {
     printf("hello");
     fork();
     printf("world");
     return 0;
 }
 
//这里有坑,printf打印没有'\n',hello还在缓存区,子进程会把这个一同复制过去。
linux@ubuntu:~/test/test04$ ./a.out 
helloworldlinux@ubuntu:~/test/test04$ helloworld
2.加上'\n'
int main(int argc, const char *argv[])
{
    printf("hello\n");//加上'\n后'
    fork();
    printf("world");
    return 0;
}                                       

linux@ubuntu:~/test/test04$ ./a.out 
hello
worldlinux@ubuntu:~/test/test04$ world
3.多个fork
int main(int argc, const char *argv[])
{
    fork();
    fork();
    printf("hello\n");
    printf("world");

    return 0;
}                                         

linux@ubuntu:~/test/test04$ ./a.out 
hello
worldlinux@ubuntu:~/test/test04$ hello
worldhello
worldhello
world
int main(int argc, const char *argv[])
{
    int i;
    for(i=0;i<3;i++)
    {
        printf("hello world\n");          
        fork();//放在下面
    }
    return 0;
}

linux@ubuntu:~/test/test04$ ./a.out 
hello world
hello world
hello world
linux@ubuntu:~/test/test04$ hello world
hello world
hello world
hello world
int main(int argc, const char *argv[])
{
    int i;
    for(i=0;i<3;i++)
    {
        fork();//放在上面
        printf("hello world\n");          
    }
    return 0;
}

linux@ubuntu:~/test/test04$ ./a.out 
hello world
hello world
hello world
linux@ubuntu:~/test/test04$ hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world

4.查看父子关系

int main(int argc, const char *argv[])
{
    printf("hello\n");
    fork();
    printf("world");
    while(1);

    return 0;
}                                          

另开一个终端

linux@ubuntu:~$ ps aux

linux      4070 48.2  0.0   4504   744 pts/0    R+   19:59   0:03 ./a.out
linux      4071 48.2  0.0   4504    80 pts/0    R+   19:59   0:03 ./a.out

linux@ubuntu:~$ ps -ef

linux      4083   2220 46 20:02 pts/0    00:00:05 ./a.out
linux      4084   4083 46 20:02 pts/0    00:00:05 ./a.out

写时拷贝

fork函数创建子进程时要复制父进程的资源,但是子进程并不一定会用到 这些资源,所以说采用一种方式:当创建完子进程以后,子进程先共享父进程的资源。如果双方有一方去修改内容,修改之前复制一份到子进程。这就叫做写时拷贝技术。

文件共享

如果在fork之间打开了一些文件,获取了一些文件描述符。那么fork之后父进程和子进程公用这些fork之前获取的文件描述符。那么在操作过程之中,有可能造成读写指针互相影响。

利用进程复制文件

📝练习:使用两个进程共同去完成复制一个文件,将一个文件拷贝到另一个文件中

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	//使用文件IO打开文件 ,获取文件的大小
	int fd = open(argv[1],O_RDONLY);
	if (-1 == fd)
	{
		perror("open");
		return -1;
	}
	int fd1 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);
	if (-1 == fd1 )
	{
		perror("open1");
		return -1;
	}
	
	//获取文件大小利用偏移量
	int len = lseek(fd,0,SEEK_END);

	//需要一个从开头一个从中间,需要找到中间
	len = len / 2;

	//开始创建子进程
	//子进程从中间开始读取,从中间开始写入
	//父进程就从开头读取,从开头写入,到中间截至

	pid_t pid = fork();
	if(-1 == pid)
	{
		perror("fork");
		return -1;
	}
	if (pid == 0 )
	{
		//子进程  ---》中间
		close(fd);//避免再读取时对父进程读写指针造成影响
		close(fd1);
		fd = open(argv[1],O_RDONLY);
		if (-1 == fd)
		{
			perror("open");
			return -1;
		}
		fd1 = open(argv[2],O_WRONLY);
		if( -1 == fd1)
		{
			perror("open1");
			return -1;
		}
		//将被读文件的读写指针偏移到中间
		lseek(fd,len,SEEK_SET);
		//将被写入的文件的读写指针偏移到中间
		lseek(fd1,len,SEEK_SET);
		char buf[123] = {0};//接收读到的输入,写入数据时使用
		ssize_t ret = 0;//来接手read 的返回值
		while((ret = read(fd,buf,123)))
		{
			if( -1 == write(fd1,buf,ret))
			{
				perror("write");
				return -1;
			}
		}
		close(fd);
		close(fd1);
		exit(0);

	}else if (pid > 0)
	{
		//父进程 ---》从开始
		lseek(fd,0,SEEK_SET);
		lseek(fd1,0,SEEK_SET);
		char buf[123] = {0};
		ssize_t  ret = 0;
		while(len)//以工作量为循环条件,每次减去已经读到的
		{
			if (len >= 123)
			{
				ret = read(fd,buf,123);
			}else{
				ret = read(fd,buf,len);	
			}
			write(fd1,buf,ret);
			len = len - ret;
		}
	}

获取进程ID接口

getpid:
  1. 功能:获取自己的进程ID

  2. 头文件:

    #include <sys/types.h>

    #include <unistd.h>

  3. 原型: pid_t getpid(void)

  4. 参数:无

  5. 返回值:

    成功返回自己的进程ID

    无失败

getppid:
  1. 功能:获取父进程的进程ID

  2. 原型: pid_t getppid(void);

  3. 参数:无

  4. 返回值:

    返回自己父进程的ID号

    无失败

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
        pid_t pid = fork();
        if(-1 == pid)
        {
            perror("fork");
            return -1;
        }
        if ( 0 == pid)
        {
            printf("我是子进程\n");
            printf("我是子进程我的进程号为%d\n",getpid());
            printf("我是子进程我的父进程进程号为%d\n",getppid());

        }else if(pid > 0 )
        {
             printf("我是父进程\n");
             printf("我是父进程,我的子进程的ID号为%d\n",pid);
             sleep(1);
             printf("我是父进程,我的进程ID号为%d\n",getpid());
             printf("我是父进程,我的父进程的ID号为%d\n",getppid());
        }
} 

三个结束进程的函数

exit:
  1. 头文件:#include <stdlib.h>
  2. 原型: void exit(int status);
  3. 功能:结束一个进程,先释放缓冲区
  4. 参数:status :结束进程时的状态,同return 正常结束用0 非正常结束-1
  5. 返回值:无
_exit:
  1. 头文件: #include <unistd.h>
  2. 原型: void _exit(int status);
  3. 功能:结束一个进程,不会释放缓冲区直接结束
  4. 参数:status :结束进程时的状态,同return 正常结束用0 非正常结束-1
  5. 返回值:无
atexit:
  1. 头文件:#include <stdlib.h>

  2. 原型: int atexit(void (*function)(void));

  3. 功能: 注册一个进程结束后的运行函数 //当进程结束时会调用function这个函数

  4. 参数:指向返回值时void类型参数时void 的一个函数指针

  5. 返回值:

    成功返回0

    失败返回一个非0值

代码:

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

//准备一个void fun(void) 的函数

void fun(void)
{
    printf("hello wrold\n");
}

int main(int argc, const char *argv[])
{
    //开始注册函数
    atexit(fun);
    sleep(3);
    return 0;
}

孤儿进程:

父进程优先于子进程结束。子进程失去父亲之后,子进程会认为1号进程是自己的父进程。那么1号进程负责回收和管理子进程。如果说很多子进程都认为1号进程为他的父进程。1号进程的负担会很大,所以,我们在编写代码时尽量让父进回收完子进程资源之后在结束。孤儿进程是没有危害的

僵尸进程:

子进程优先于父进程结束。子进程会认为该父进程会回收自己的资源。但是父进程一直在忙于自己的事情未曾去回收子进程资源,子进程资源就得不到回收,但是子进程的任务已经结束了。所以说子进程就变成了僵尸进程。僵尸进程是有危害的

📢注意:以后在写fork时都需要回收子进程资源。

#include <stdio.h>
#include <sys/types.h>
 #include <unistd.h>
int main(int argc, const char *argv[])
{
        pid_t pid = fork();
        if(-1 == pid)
        {
            perror("fork");
            return -1;
        }
        if(pid == 0)
        {
            exit(0);//子进程结束父进程未结束,子进程变成了僵尸进程
        }else if(pid > 0 )
        {
            while(1){
                sleep(1);
            }
        }
    return 0;
}

处理僵尸进程:

wait :
  1. 头文件:

    #include <sys/types.h>

    #include <sys/wait.h>

  2. 原型: pid_t wait(int *wstatus);

  3. 功能:阻塞等待回收任意一个子进程资源

  4. 参数:wstatus :进程结束的状态,一般不考虑进程结束的状态直接填写NULL 缺省模式,如果说想接收一下exit的返回的状态值,它会将exit返回的状态值放入wstatus地址中的8-15位的地方 。

    linux@ubuntu:~/test/IO/test$ ./a.out 
    0x7ffd5b0d5090
    linux@ubuntu:~/test/IO/test$ ./a.out 
    0x7ffeb92b3760
    linux@ubuntu:~/test/IO/test$ ./a.out 
    0x7ffdb51c1c20
    
    0x0000000
    
  5. 返回值:

    成功会返回接收到的进程的ID号

    失败会返回 -1;

  6. 注意:如果父进程调用wait来回收资源,那么会阻塞等待。会降低父进程的工作效率。

  7. 代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
        pid_t pid = fork();
        if(-1 == pid)
        {
            perror("fork");
            return -1;
        }
        if(pid == 0)
        {
            sleep(5);
            exit(0);//子进程结束父进程未结束,子进程变成了僵尸进程
        }else if(pid > 0 )
        {
            //父进程中
            printf("父进程运行\n");       
            printf("接受成功,接收到子进程的ID号为%d\n",wait(NULL));
        }
    return 0;
}

waitpid:
  1. 头文件:

    #include <sys/types.h>

    #include <sys/wait.h>

  2. 原型: pid_t waitpid(pid_t pid, int *wstatus, int options);

  3. 功能:回收子进程资源,可以不阻塞回收也可以指定回收哪一个。

  4. 参数:

    1. pid > 0 :回收指定的子进程
    2. pid == -1;回收任意一个子进程 如果采用阻塞的方式与wait一样
    3. pid == 0 时:回收同进程组中的任意一个子进程
    4. pid < -1 时:回收同进程组中ID号等于pid绝对值的子进程 注意:-号代表同组,对pid进行取绝对值,|pid|==正数
    5. wstatus :回收到的子进程结束时返回的状态
    6. options :操作方式
    7. 0:阻塞回收
    8. WNOHANG :非阻塞方式回收 如果去回收时,没有子进程结束,立马返回,如果说已经有 子进程结束了立马回收。
  5. 返回值:

    成功返回回收到的子进程的ID号

    失败返回-1;

  6. 如果: WNOHANG 非阻塞方式去回收时,子进程如果没有结束则返回0

    使用最多还是 waitpid(-1;NULL;WNOHANG);

  7. 注意:需要频繁去调用函数去查看子进程结束与否

  8. 代码:

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

int main(int argc, const char *argv[])
{
        pid_t pid = fork();
        if(-1 == pid)
        {
            perror("fork");
            return -1;
        }
        if(pid == 0) //子进程
        {
            sleep(5);
            exit(0);//子进程结束父进程未结束,子进程变成了僵尸进程
        }else if(pid > 0 )//父进程
        {
            pid_t pid1 = 0;
            while(1)
            {
                printf("父进程在干自己的事情\n");
                if( 0 < (pid1 = waitpid(-1,NULL,WNOHANG)))
                {
                    printf("回收到了子进程资源,子进程的ID号为%d\n",pid1);
                }
                sleep(1);
            }
        }
    return 0;
}

守护进程

是一种特殊的进程机制,默默的对我的工作进行服务的进程。是一个后台进程。init进程就是一个守护进程。后台进程:只允许向终端写入数据,不允许向终端读取数据。如果一旦对终端输入进行获取,那么就会立即终止后台进程。要脱离终端的管理,脱离会话的管理。守护进程一般用于例如:服务器、http、tftp、ftp

附加:ctrl +c 给前台的所有进程发一个终止的信号(如果父进程结束了,子进程被1号进程收养了,变成孤儿进程,那么ctrl+c就和子进程没有关系了)这时候就只能用kill

创建守护进程是有固定步骤的

  1. 变成孤儿进程。()kill:给进程发一个信号

  2. 创建一个新的会话,变成会话首进程 setsid

    头文件: #include <sys/types.h> #include <unistd.h>

    原型: pid_t setsid(void);

    功能:创建一个新的会话,并且创建者称为会话的首进程

    参数:无

    返回值:成功返回一个新的会话ID 失败返回-1

  3. 修改默认工作目录文件 chdir

    头文件:#include <unistd.h>

    原型:int chdir(const char *path);

    功能:修改工作路径

    返回值:成功返回0 失败返回-1

  4. 给与最高文件权限 umask

    头文件:#include <sys/types.h> #include <sys/stat.h>

    原型: mode_t umask(mode_t mask);

    参数:想要修改的掩码值

    返回值:一定会成功返回修改完毕的掩码值

  5. 关闭文件描述符 close(0)(0代表终端);注意如果说也不会对终端进行操作,请关闭所有文件描述符

  6. 开始做守护事件 ,如日志文件的写入

  7. 附加:getdtablesize

    功能:计算该进程的文件描述符的个数

    头文件:#include <unistd.h>

    原型:int getdtablesize(void);

    参数:无

    返回值:成功返回文件描述符的个数

  8. 代码:

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

int main(int argc, const char *argv[])
{
    //创建一个孤儿进程
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(0 == pid)
    {
        //子进程
        //创建一个会话,自己成为自己的主人
        if(-1 ==setsid())
        {
            perror("setsid");
            return -1;
        }
        //修改工作目录
        if(-1 == chdir("/"))
        {
            perror("chdir");
            return -1;
        }
        //修改文件掩码

        umask(0);

        //关闭文件描述符
        close(0);
        close(1);
        close(2);

        FILE * fp = fopen("./1.txt","a");
        while(1)
        {
            //写日志文件
            fprintf(fp,"Hello world\n");
            fflush(fp);

            sleep(1);
        }
    }
    //不去管父进程,父进程自动结束

    return 0;
}

EXEC族

一些函数的集合。该进程集合中的,所有的函数都是完成一个功能,只是函数用法不同而已。实现的功能为:用其他进程替换该进程,避免空间重新释放和申请

system :功能:在进程中调用其他进程

execl
  1. 原型:int execl(const char *path, const char *arg, ... /* (char *) NULL */);

  2. 参数:

    ​ path :可执行文件的路径

    ​ arg : 给可执行文件传的参数

    ​ 可以是多个参数:固定规则: "可执行文件的名字" ,"参数1" ,"参数2" .。。,NULL结尾

  3. 返回值:

    ​ 如果执行成功的话,不做返回

    ​ 如果失败返回 -1

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

int main(int argc, const char *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(0 == pid)
    {
        printf("子进程在做自己的事情\n");
        sleep(3);
        printf("子进程任务结束\n");
        //替换其他进程继续使用该空间--ls

        if(execl("/bin/ls","ls","-l",NULL) == -1)
        {
            perror("execl");
            return -1;
        }
        printf("子进程在做自己的事情\n");
        //这条语句不会打印,因为子进程被替换掉了
    }
    else if(pid > 0)
    {
        //wait
        wait(NULL);
    }
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
子进程在做自己的事情
子进程任务结束
总用量 655728
-rwxrwxrwx 1 linux linux 671444992 826 17:47 1.txt
-rwxr-xr-x 1 linux linux      8512 827 07:44 a.out
-rwxrwxrwx 1 linux linux       724 827 07:44 test.c
execlp
  1. 原型:int execlp(const char *file, const char *arg, ... /* (char *) NULL */);

  2. 功能不同点:只能调用PATH环境变量中的可执行文件

  3. 参数:

    ​ file :可执行文件的名字

    ​ arg : 给可执行文件传的参数

    ​ 可以是多个参数:固定规则: "可执行文件的名字" ,"参数1" ,"参数2" .。。,NULL结尾

  4. 返回值:

    ​ 如果执行成功的话,不做返回

    ​ 如果失败返回 -1

  5. 代码

execv
  1. 原型:int execv(const char *path, char *const argv[]);

  2. 参数:

    ​ path:目标的路径名称

    ​ arg : 给可执行文件传的参数

    ​ 可以是多个参数:固定规则: {"可执行文件的名字" ,"参数1" ,"参数2" ,NULL }结尾

  3. 返回值:

    ​ 如果执行成功的话,不做返回

    ​ 如果失败返回 -1

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(0 == pid)
    {
        printf("子进程在做自己的事情\n");
        sleep(3);
        printf("子进程任务结束\n");
        //替换其他进程继续使用该空间--ls
       /* if(execl("/bin/ls","ls","-l",NULL) == -1)//execl
        {
            perror("execl");
            return -1;
        }*/
        
        /*if(execlp("ls","ls","-l",NULL) == -1)//execlp
        {
            perror("execlp");
            return -1;
        }*
        
        char * arg[] ={"ls","-l",NULL} ;//execv
        if(execv("/bin/ls",arg) == -1)
        {
            perror("execl");
            return -1;
        }
        printf("子进程在做自己的事情\n");
        //这条语句不会打印,因为子进程被替换掉了
    }
    else if(pid > 0)
    {
        //wait
        wait(NULL);
    }
    return 0;
}