首页 > 其他分享 >system函数的风险和解决

system函数的风险和解决

时间:2023-07-10 12:22:11浏览次数:32  
标签:__ 函数 system posix 解决 sa include sigaction

system函数的风险和解决

源码摘录

/* Execute LINE as a shell command, returning its status.  */
static int
do_system (const char *line)
{
  int status = -1;
  int ret;
  pid_t pid;
  struct sigaction sa;
#ifndef _LIBC_REENTRANT
  struct sigaction intr, quit;
#endif
  sigset_t omask;
  sigset_t reset;

  sa.sa_handler = SIG_IGN;
  sa.sa_flags = 0;
  __sigemptyset (&sa.sa_mask);

  DO_LOCK ();
  if (ADD_REF () == 0)
    {
      /* sigaction can not fail with SIGINT/SIGQUIT used with SIG_IGN.  */
      __sigaction (SIGINT, &sa, &intr);
      __sigaction (SIGQUIT, &sa, &quit);
    }
  DO_UNLOCK ();

  __sigaddset (&sa.sa_mask, SIGCHLD);
  /* sigprocmask can not fail with SIG_BLOCK used with valid input
     arguments.  */
  __sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask);

  __sigemptyset (&reset);
  if (intr.sa_handler != SIG_IGN)
    __sigaddset(&reset, SIGINT);
  if (quit.sa_handler != SIG_IGN)
    __sigaddset(&reset, SIGQUIT);

  posix_spawnattr_t spawn_attr;
  /* None of the posix_spawnattr_* function returns an error, including
     posix_spawnattr_setflags for the follow specific usage (using valid
     flags).  */
  __posix_spawnattr_init (&spawn_attr);
  __posix_spawnattr_setsigmask (&spawn_attr, &omask);
  __posix_spawnattr_setsigdefault (&spawn_attr, &reset);
  __posix_spawnattr_setflags (&spawn_attr,
			      POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);

  ret = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
		       (char *const[]){ (char *) SHELL_NAME,
					(char *) "-c",
					(char *) line, NULL },
		       __environ);
  __posix_spawnattr_destroy (&spawn_attr);

  if (ret == 0)
    {
      /* Cancellation results in cleanup handlers running as exceptions in
	 the block where they were installed, so it is safe to reference
	 stack variable allocate in the broader scope.  */
#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
      struct cancel_handler_args cancel_args =
      {
	.quit = &quit,
	.intr = &intr,
	.pid = pid
      };
      __libc_cleanup_region_start (1, cancel_handler, &cancel_args);
#endif
      /* Note the system() is a cancellation point.  But since we call
	 waitpid() which itself is a cancellation point we do not
	 have to do anything here.  */
      if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
	status = -1;
#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
      __libc_cleanup_region_end (0);
#endif
    }
  else
   /* POSIX states that failure to execute the shell should return
      as if the shell had terminated using _exit(127).  */
   status = W_EXITCODE (127, 0);

  DO_LOCK ();
  if (SUB_REF () == 0)
    {
      /* sigaction can not fail with SIGINT/SIGQUIT used with old
	 disposition.  Same applies for sigprocmask.  */
      __sigaction (SIGINT, &intr, NULL);
      __sigaction (SIGQUIT, &quit, NULL);
      __sigprocmask (SIG_SETMASK, &omask, NULL);
    }
  DO_UNLOCK ();

  if (ret != 0)
    __set_errno (ret);

  return status;
}

主要步骤有:忽略信号SIGINT和SIGQUIT,阻塞信号SIGCHLD,创建子进程执行shell并执行命令,等待子进程(退出),恢复信号;

主要问题和注意事项

低效

#include <cstdlib>
#include <chrono>
#include <stdio.h>

int main()
{
	auto lambda_now_ms = []()
	{ 
		return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
	};
	
	auto start1 = lambda_now_ms();
	auto start2 = lambda_now_ms();
	for (int i = 0; i < 1000; ++i)
	{
		std::system("touch test_file");
		std::system("rm test_file");
	}
	auto end = lambda_now_ms();

	int diff1 = start2 - start1;
	int diff2 = end - start2;
	printf("diff: %d/%d\n", diff2, diff1);
	return 0;
}
$ g++ test.cpp
$ ./a.out
diff: 3372/0

可以看到,通过system函数执行文件系统命令的耗时达到了毫秒级别。

将创建文件和删除文件的操作改为直接实现的方式,如:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
...
open(file, O_CREAT | O_RDWR | O_TRUNC, 0666);
unlink(file);

此时,执行的结果为:

$ g++ test.cpp
$ ./a.out
diff: 22/0

可以看到,两者速度的差别达到了两个数量级;

信号处理

为什么要忽略信号SIGINT和SIGQUIT

一个解释:这两个信号在终端产生,并且是发送给所有前台进程组中的进程的。当调用system()运行一个前台程序时,如果此时触发了信号,则这个信号只应该影响system()调用的程序,而不应该影响调用system()的程序。

一个示例如下:

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

int main()
{
    system("vim");
    printf("end\n");

    while (true)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return 0;
}

程序运行后,按下"Ctrl C",退出vim后,发现主程序继续运行。如果system()没有处理SIGINT信号,则当vim退出后,主程序也会一并退出。

为什么要阻塞信号SIGCHLD

这是因为system()的实现中使用了waitpid()来获取进程执行的结果。可以参考man 2 waitpid。

主要原因是如果一个进程忽略了SIGCHLD(这也是默认行为),则其子进程在结束后,waitpid会等待子进程的结束,然后返回错误(不会得到正常的返回值)。

返回值

环境变量

解决

标签:__,函数,system,posix,解决,sa,include,sigaction
From: https://www.cnblogs.com/amazzzzzing/p/17540752.html

相关文章

  • 粒子群算法PSO优化LSSVM最小二乘支持向量机惩罚参数c和核函数参数g,用于回归预测,有例子
    粒子群算法PSO优化LSSVM最小二乘支持向量机惩罚参数c和核函数参数g,用于回归预测,有例子,易上手,简单粗暴,直接替换数据即可。仅适应于windows系统。质量保证,完美运行。这段程序主要是一个基于粒子群优化算法(ParticleSwarmOptimization,PSO)的支持向量机(SupportVectorMachine,SVM)......
  • 当函数遇上图片,比如Filter
    Filter函数可以说是包揽了一切查找,嗯,够辛苦。有木有想过,如果Filter遇上图片,又是怎样的风景呢?就是这个样几滴……这里的图片可以是两种,一种是由Image函数生成的,另一种是放置在单元格中的图片。其实,这并不是Filter的独享,因为图片作为单元格对象,所以几乎可以说是所有函数,都能对其进行......
  • Python | os.makedirs函数的使用
    概述os.makedirs()方法用于递归创建目录。如果子目录创建失败或者已经存在,会抛出一个OSError的异常,Windows上Error183即为目录已经存在的异常错误。如果第一个参数path只有一级,则mkdir()函数相同。语法makedirs()方法语法格式如下:os.makedirs(path,mode=0o777)参......
  • java项目 报错 maven jdk.tools 缺失 解决方法
    一、解决方法配置文件pom.xml<dependency><groupId>jdk.tools</groupId><artifactId>jdk.tools</artifactId><version>1.7</version><scope>system</scope><systemPath>${......
  • 解决jQuery手机切换的具体操作步骤
    实现jQuery手机切换教程介绍在这篇文章中,我将教会你如何使用jQuery实现手机切换效果。首先,我将提供一张表格,展示实现这一功能的步骤。然后,我将逐步解释每个步骤需要做什么,并提供相应的代码和注释。实现步骤步骤描述1.创建HTML结构2.添加CSS样式3.编写JavaScr......
  • 解决jQuery动画透明度的具体操作步骤
    jQuery动画透明度介绍jQuery是一个快速、简洁的JavaScript库,被广泛用于网页开发中。它提供了丰富的API,使得操作DOM元素变得更加简单。其中,jQuery动画是其最受欢迎的特性之一,可以用来创建各种各样的动画效果,包括透明度的动画。透明度是指一个元素的可见程度,它可以控制元素的显示......
  • 解决jQuery点击事件的具体操作步骤
    jQuery点击事件实现指南简介在Web开发中,使用jQuery可以简化和加快JavaScript代码的编写。点击事件是最常用的交互操作之一,通过它可以实现当用户点击某个元素时触发相应的功能或事件。本文将教会刚入行的小白如何使用jQuery实现点击事件。流程下面是使用jQuery实现点击事件的基......
  • jQuery 需要判断很多怎么写比较好 这个问题怎么解决?
    项目方案:优化jQuery大量判断的实现项目背景在现代web开发中,jQuery是一个广泛使用的JavaScript库,它提供了简洁高效的API来操作HTML文档、处理事件、执行动画等。然而,当我们需要进行大量判断操作时,jQuery的代码可能会变得冗长和难以维护。本项目的目标是提出一种优化......
  • 解决jQuery do while的具体操作步骤
    如何使用jQuery实现dowhile循环引言在开发网页应用程序时,经常需要使用循环语句来重复执行一段代码。而jQuery是一个流行的JavaScript库,提供了简洁而强大的方法来操作HTML元素、处理事件以及执行动画等。本文将介绍如何使用jQuery实现dowhile循环。一、整个流程......
  • 解决intellij idea spring boot 安装的具体操作步骤
    IntellijIDEASpringBoot安装指南简介IntellijIDEA是一款强大的Java集成开发环境(IDE),而SpringBoot是一种用于简化Spring应用程序开发的框架。本文将指导你如何在IntellijIDEA中安装和配置SpringBoot。安装步骤下面的表格展示了IntellijIDEASpringBoot安装的步骤及相应......