Linux系统编程-进程创建(fork)、外部程序调用(exec)

简介: 在linux中fork函数是非常重要的函数,它可以从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

1. fork函数介绍

在linux中fork函数是非常重要的函数,它可以从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

fork函数的返回值如下:
1、在父进程中,fork返回新创建的子进程的PID号。
2、在子进程中,fork返回0;
3、如果出现错误,fork返回一个负值。
因此可以通过返回值来判断是父进程还是子进程。

fork函数创建子进程的过程:
使用fork函数得到的子进程是父进程的一个复制品,它从父进程继承了进程的所有资源,相当于就是父进程的一个副本。

#include <unistd.h>
pid_t fork(void); 制作分身
函数功能: 创建新的子进程. 子进程是父的进程一个副本. (分身)
返回值:  >0表示父进程  ==0表示子进程

示例代码:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
    int a=888;
    //创建新的子进程
    pid_t pid=fork();
    if(pid==0) //子进程
    {
        a=999;
        printf("子进程_a=%d\n",a);
        printf("子进程的PID号:%d\n",getpid());
        printf("当前子进程的父进程PID号:%d\n",getppid());
    }
    else
    {
        sleep(1);
    }
    return 0;
}

2. wait函数

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
函数功能: 随机等待一个子进程退出.
如果等待的子进程正常结束,返回值就是该子进程的pid号
如果父进程没有子进程正在运行,wait函数会立即返回,返回值为-1

pid_t waitpid(pid_t pid, int *status, int options);
函数功能: 等待指定的子进程退出.

正常的多进程并发设计,父进程要负责清理子进程的空间:

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

int main()
{
    pid_t pid=fork();
    if(pid==0)
    {
        printf("子进程运行成功.pid=%d\n",getpid());
    }
    else
    {
        pid=wait(NULL); //等待子进程退出,清理子进程的空间
        printf("退出的子进程的pid=%d\n",pid);
    }
    return 0;
}

3. exec系列函数

exec系列函数是用于启动一个新的进程,将新的进程启动成功之后会覆盖原进程.

#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[]);

p表示支持从环境变量里搜索可执行文件.

示例代码:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
    //ls -l
    //execl("/bin/ls","ls","-l",NULL);
    //execlp("ls","ls","-l",NULL);

    char *cmd[]={"ls","-l",NULL};
    //execv("/bin/ls",cmd);
    //execvp("ls",cmd);

    char *cmd_path[]={"wbyq=666","abcd=888",NULL};
    execle("/bin/ls","ls","-l",NULL,cmd_path);

    printf("exec函数执行失败.\n");
    return 0;
}

4. system函数

system函数用于启动新的子进程,这个函数内部就是使用fork+exec+wait函数组合实现的。
说明这个system函数是阻塞的,必须等待子进程执行完毕之后才会执行父进程的代码。

#include <stdlib.h>
int system(const char *command);

示例代码:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
    printf("开始执行.\n");
    system("sleep 5");
    printf("执行结束.\n");
    return 0;
}

5. popen函数

#include <stdio.h>
FILE *popen(const char *command, const char *type);

函数功能: 启动一个新的进程--类似于fopen --dup2函数
启动成功的返回值的指针就指向该进程的标准输出。

int pclose(FILE *stream);
函数功能: 释放空间
示例代码:
#include <stdio.h>
int main()
{
    FILE *fp;
    char buff[1024+1];
    int cnt;
    fp=popen("/bin/ls *.sh","r");
    cnt=fread(buff,1,1024,fp);
    buff[cnt]='\0';
    printf("buff=%s\n",buff);
    pclose(fp);
    return 0;
}

6. pkill 命令

(1) pkill 命令支持一次性杀死某用户的所有进程。
$ pkill -u <用户名>
(2) pkill 命令支持一次杀死指定名称的所有进程。
$ pkill <应用名称>
(3) 使用 killall 命令一次杀死指定名称的所有进程
$ killall <应用名称>
(4) 杀死父进程创建的所有子进程
pkill -9 -P <父进程 PID>

7. 案例: 使用fork函数创建5个子进程同时运行

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

int main()
{
    int i;
    pid_t pid;
    for(i=0;i<5;i++)
    {
        pid=fork(); //创建一个子进程
        if(pid==0)break; //如果是子进程就直接退出循环
    }
    if(i==5) //父进程
    {
        sleep(5);
        char cmd_buff[100];
        sprintf(cmd_buff,"pkill -9 -P %d",getpid());
        system(cmd_buff);
        while(1)
        {
            pid=wait(NULL);
            if(pid==-1)break; //当父进程没有子进程的时候该函数就返回-1
            else printf("%d 子进程退出成功.\n",pid);
        }
        printf("父进程正常结束.\n");
    }
    else //子进程
    {
        while(1)
        {
            sleep(1);
            printf("当前运行的子进程pid=%d\n",getpid());
        }
    }
    return 0;
}

8. 案例: 实现自动切换(2秒)显示指定目录下的所有图片

利用eog命令,配合今天学习的进程知识点,做出一个ppt播放效果。
思路: 父进程扫描目录,得到目录下的文件名称,在传递给子进程,子进程调用eog命令实现图片显示,父进程里2秒钟之后就杀死子进程,再读取目录下下一个文件,再传递给子进程………

image-20211210104224128

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

int main(int argc,char **argv)
{
    if(argc!=2)
    {
        printf("./a.out <图片目录/>\n");
        return 0;
    }
    /*1. 打开目录*/
    DIR *dir=opendir(argv[1]);
    if(dir==NULL)
    {
        printf("%s 目录打开失败.\n",argv[1]);
        return 0;
    }
    /*2. 创建管道文件*/
    int fds[2];
    pipe(fds);

    pid_t pid;
    struct dirent *dir_info;
AA:
    /*循环遍历目录*/
    dir_info=readdir(dir);
    if(dir_info==NULL)
    {
        printf("图片显示完毕.\n");
        exit(0); //表示图片显示完毕
    }
    /*创建子进程*/
    pid=fork();
    if(pid==0) //子进程
    {
        char file_name[100+1];
        int cnt;
        //从管道的读端读取数据
        cnt=read(fds[0],file_name,100);
        file_name[cnt]='\0';
        printf("正在显示的图片:%s\n",file_name);
        //启动新的进程
        execlp("eog","eog",file_name,NULL);
    }
    else  //父进程
    {
        //判断是否是图片
        if(strstr(dir_info->d_name,".jpg"))
        {
            char *p;
            //组合图片的路径
            p=malloc(strlen(argv[1])+strlen(dir_info->d_name)+1);
            sprintf(p,"%s%s",argv[1],dir_info->d_name);
            //向管道的写端写数据
            write(fds[1],p,strlen(p)); 
            //延时2秒
            sleep(2);
            //杀死父进程创建的所有子进程
            char cmd_buff[100];
            sprintf(cmd_buff,"pkill -9 -P %d",getpid());
            system(cmd_buff);
            //清理子进程的空间
            wait(NULL);
        } 
        //继续显示下一张图片
         goto AA;
    }

    return 0;
}
目录
相关文章
|
4天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
19 3
|
4天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
16 2
|
4天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
19 3
|
7天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
23 6
|
7天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
32 6
|
5月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
126 13
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
162 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
3月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
4月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
171 1