【Linux】进程exec函数族以及守护进程

2024-05-05 00:04
文章标签 linux 函数 进程 守护 exec

本文主要是介绍【Linux】进程exec函数族以及守护进程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.exec函数族

1.exec函数族的应用

        在shell下敲shell的命令都是在创建shell的子进程。而我们之前学的创建父进程和子进程代码内容以及通过pid与0的关系来让父子进程执行不同的代码内容都是在一个代码文件里面,而shell是如何做到不在一个文件里面写代码使之成为子进程的呢?

        答案是使用了exec函数族

假如现在有段在shell下敲的名为test的代码,执行过程为:父进程(shell)fork()出一个子进程后,子进程调用exec函数族执行test,此时原进程的内容就会被覆盖,执行test的内容,就实现了创建新的进程并在该进程中执行不同的程序;

这些函数允许将当前进程内容替换为新的可执行文件,从而实现进程的代码和数据的切换。

进程调用exec函数族执行某个程序

进程当前内容被指定的程序替换

实现让父子进程执行不同的程序 :

●父进程创建子进程  

●子进程调用exec函数族  

●父进程不受影响

2.exec函数族的一些常见成员

(1)excel / excelp(熟练)

#include  <unistd.h>
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);

 成功时执行指定的程序;失败时返回EOF

 path   执行的程序名称,包含路径

 arg…  传递给执行的程序的参数列表

 file   执行的程序的名称,在PATH中查找(PATH为linux的环境变量,添加到系统的环境变量后,在系统的任意路径都能直接输入程序名称执行,不用输入路径)

execl : 以参数列表的方式执行新程序。需要指定可执行文件的路径,以及传递给新程序的参数列表。

进程创建 – execl(p) – 示例:

 ⭕ 执行ls命令,显示/etc目录下所有文件的详细信息

#include <stdio.h>
#include <unistd.h>int main(int argc, const char *argv[])
{if(execl("/bin/ls","ls","-a","-l","/etc",NULL)<0){perror("execl");}return 0;
}
#include <stdio.h>
#include <unistd.h>int main(int argc, const char *argv[])
{if(execlp("ls","ls","-a","-l","/etc",NULL)<0){perror("execl");}return 0;
}

分析:

execl中:

"/bin/ls"为执行的程序名称,在/bin路径下有个叫ls的程序;

"ls"是第0个参数,第0个参数通常是程序的名称,第0个参数必须要写,虽然它没有使用;

"-a","-l","/etc"都是传递给程序的参数;

execlp中:除了直接传入程序名称在系统路径中查找以外,其余和execl相同;

注意:两个函数区别在于execlp不需要写文件名全路径,在系统路径PATH查找

            最后一个参数都必须用空指针(NULL)作结束

            进程当前内容被指定的程序替换,但进程号不变

            第0个参数必须要写,虽然它没有使用

(2)execv / execvp (熟练)

  #include  <unistd.h>int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);

成功时执行指定的程序;失败时返回EOF  ;

arg… 封装成指针数组的形式;

这两个就是把参数列表封装成了一个数组;

进程创建 – execv(p) – 示例

⭕执行ls命令,显示/etc目录下所有文件的详细信息

伪代码如下:

  char  *arg[] = {“ls”, “-a”, “-l”, “/etc”, NULL};if  (execv(“/bin/ls”, arg) < 0){perror(“execv”);}  if  (execvp(“ls”, arg) < 0){perror(“execvp”);}  

注意:末尾的NULL照样不能省;

3.system(exec的简略版本) (熟练)

内部使用了 exec 函数族的一种方式来执行 shell 命令

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

成功时返回命令command的返回值;失败时返回EOF

当前进程system 函数会创建一个子进程来执行 shell 命令,等待执行结束后才继续执行

system 函数一般用来执行shell命令,并不适用于直接执行可执行程序,一般使用exec函数族来执行可执行程序。

#include <stdlib.h>int main(int argc, const char *argv[])
{system("/bin/ls -a -l /etc");return 0;
}

二.守护进程

1.守护进程的特点(了解)

(1)守护进程

守护进程(Daemon Process)是Linux三种进程类型之一;

是 Linux 中的后台服务进程;

是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件;

(2)守护进程特点

始终在后台运行

独立于任何终端

周期性的执行某种任务或等待处理特定事件

它是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭

举例:

http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。

2.会话、控制终端(了解)

进程组(Process Group): 进程集合,每个进程组有一个组长(Leader),其进程 ID 就是该进程组 ID。

会话(Session): 进程组集合,每个会话有一个组长,其进程 ID 就是该会话组 ID。

控制终端(Controlling Terminal):每个会话可以有一个单独的控制终端,与控制终端连接的 Leader 就是控制进程(Controlling Process)。

即进程的集合为进程组,进程组集合为会话组 ,会话组的控制终端为控制进程;

3.创建守护进程流程(熟练)

简便地创建守护进程: nohup 命令

nohup  xxxx  &

nohup在某些情况下是方便的,但是需要创建更稳定和更高级需求的守护进程时并不推荐nohup;

接下来我们介绍学习另外的方法:

(1)创建子进程,父进程退出

 if (fork() > 0)  
{exit(0);
}

子进程变成孤儿进程,被init进程收养  

子进程在后台运行

(2)子进程创建新会话

setsid函数:
 

#include <unistd.h>pid_t setsid(void);

成功:返回调用进程的会话ID;失败:-1,设置errno。

作用:用于创建一个新的会话,并将调用进程设置为这个新会话的领导进程(session leader),同时也是一个新进程组的组长。这使得进程完全脱离原始的控制终端,而且它的子进程将成为这个新会话和进程组的成员
调用了setsid函数的进程,既是新的会长,也是新的组长;

if(setsid()<0)
{perror("setsid:");exit(-1);
}

此时

子进程成为新的会话组长  

子进程脱离原先的终端

(3)更改当前工作目录

守护进程一直在后台运行,其工作目录不能被卸载要具有稳定性,因此最好重新设定一个稳定的工作目录;

chdir("/");
chdir("/tmp");

(4)重设文件权限掩码

umask() 是一个用于设置文件创建权限掩码的系统调用函数。当进程创建新文件或目录时,会根据当前的文件创建权限掩码来确定新文件的权限;

if(umask(0)<0)
{exit(-1);
}

 文件权限掩码设置为0;

只影响当前进程创建的新文件的权限;

(5)关闭打开的文件描述符

在脱离终端控制后,stdin / stdout / stderr无法再使用,所以需要把这三个的文件描述符关闭,标准输入(0)、标准输出(1)、和标准错误(2)

int  i;
for(i=0; i<3; i++) 
{close(i); 
}

 关闭所有从父进程继承的打开文件  

已脱离终端,stdin / stdout / stderr无法再使用

(6)步骤总结

第一步:用exit()将父进程退出,使子进程成为孤儿进程被init进程收养;
第二步:子进程利用setsid()函数创建新的会话,脱离原来的终端,成为会话组长;
第三步:使用chdir()更改工作的目录;
第四步:使用umask()重设文件权限掩码;
第五步:关闭文件描述符。
第一步可用nohup xxx  & 命令进行,但是不推荐;
第一二步是必须完成的,后面是根据自己的实际情况进行的。

4.守护进程—示例

 创建守护进程,每隔1秒将系统时间写入文件time.log

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>int main(int argc, const char *argv[])
{pid_t pid;FILE *fp;time_t t;int i;if((pid = fork())<0){perror("fork");exit(-1);}else if(pid>0){exit(0);}setsid();umask(0);chdir("/tmp");close(0);close(1);close(2);if((fp = fopen("time.log","a")) == NULL){perror("fopen");exit(-1);}while(1){time(&t);fprintf(fp,"%s\n",ctime(&t));fflush(fp);sleep(1);}return 0;
}

这篇关于【Linux】进程exec函数族以及守护进程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/960393

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

java中查看函数运行时间和cpu运行时间

android开发调查性能问题中有一个现象,函数的运行时间远低于cpu执行时间,因为函数运行期间线程可能包含等待操作。native层可以查看实际的cpu执行时间和函数执行时间。在java中如何实现? 借助AI得到了答案 import java.lang.management.ManagementFactory;import java.lang.management.Threa

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了

SQL Server中,isnull()函数以及null的用法

SQL Serve中的isnull()函数:          isnull(value1,value2)         1、value1与value2的数据类型必须一致。         2、如果value1的值不为null,结果返回value1。         3、如果value1为null,结果返回vaule2的值。vaule2是你设定的值。        如

tf.split()函数解析

API原型(TensorFlow 1.8.0): tf.split(     value,     num_or_size_splits,     axis=0,     num=None,     name='split' ) 这个函数是用来切割张量的。输入切割的张量和参数,返回切割的结果。  value传入的就是需要切割的张量。  这个函数有两种切割的方式: 以三个维度的张量为例,比如说一