zoukankan      html  css  js  c++  java
  • Linux下system()函数的实现

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <signal.h>
    
    
    /* 参考 glibc sysdeps/posix/system.c: __libc_system/do_system */
    int test_system(char* cmd)
    {
    	int status;
    	pid_t pid;
    	struct sigaction sa;
    	struct sigaction intr, quit;
    	sigset_t omask;
    
    	if (NULL == cmd) {/* glibc中当cmd为空时, 将cmd赋值为 'exit 0' */
    		return 0;
    	}
    	
    	sa.sa_handler = SIG_IGN; /* 对捕获的信号采取忽略操作 */
    	sa.sa_flags = 0;
    	sigemptyset(&sa.sa_mask); /* 清空信号集, 不包含任何信号 */
    
    	/* 忽略 SIGINT 和 SIGQUIT 信号, 为什么? 
          有谁知道吗?
    	参考:https://www.cons.org/cracauer/sigint.html */
    	sigaction(SIGINT, &sa, &intr);  /* 原有 SIGINT 处理存储到 intr 中, 用于恢复 */
    	sigaction(SIGQUIT, &sa, &quit); /* 原有 SIGQUIT 处理存储到 quit 中,用于恢复 */
    	
    	/* 阻塞SIGCHLD信号,为什么?
    	 子进程结束后,内核会给父进程发送SIGCHLD信号, 如果你注册了该信号的处理函数,并且
    	 在其中也用waipid获取了子进程结束的状态, 当信号处理函数先于system中waitpid执行,
    	 随后system函数中的waipid就会返回 No child processes 错误,无法获取到shell命令执行的结果, 只能返回 -1.
    	 阻塞SIGCHLD可以确保system中waitpid先执行, 以获取子进程结束状态,
    	 阻塞SIGCHLD期间, 如果还有其他子进程退出, 那么他们产生的SIGCHLD信号也会阻塞,
    	 但是阻塞解除后你只会收到一个SIGCHLD通知,如果你需要使用waitpid获取所以子进程状态,那么需要循环调用waitpid */
    	sigaddset(&sa.sa_mask, SIGCHLD); /* 复用面前的信号集(空的), 将SIGCHLD信号加入信号集 */
    	sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask); /* 阻塞信号集中的信号(其实信号集中只有SIGCHLD信号) */
    
    	pid = fork();
    	if (pid == (pid_t)0) {/* 子进程  */
    		const char *new_argv[4];
    		new_argv[0] = "sh";
    		new_argv[1] = "-c";
    		new_argv[2] = cmd;
    		new_argv[3] = NULL;
    	
    		/* 子进程继承父进程的信号掩码, 恢复SIGINT和SIGQUIT的信号处理操作.  */
    		sigaction(SIGINT, &intr, (struct sigaction *)NULL);
    		sigaction(SIGQUIT, &quit, (struct sigaction *)NULL);
    		sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
    	
    		/* Exec the shell.  */
    		(void)execve("/bin/sh", (char *const *) new_argv, __environ);
    		/* execve通常不会返回, 返回就说明发生错误 */
    		_exit(127); /* 子进程返回状态码127 */
    	}
    	else if (pid < (pid_t) 0) {
    		/* fork()失败返回 -1  */
    		status = -1; /* 错误查看 errno */
    	}
    	else { /* 父进程 */
    		  /* 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 (waitpid(pid, &status, 0) != pid) {/* 子进程回收 */
    			status = -1; /* 错误查看 errno */
    		}
    	}
    
    	/* 父进程信号处理恢复 */
    	sigaction(SIGINT, &intr, (struct sigaction *)NULL);
    	sigaction(SIGQUIT, &quit, (struct sigaction *)NULL);
    	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
    	
    	return status;
    }
    
  • 相关阅读:
    vim使用
    linux下编译openjdk8
    《鸟哥的Linux私房菜》学习笔记(9)——条件判断
    《鸟哥的Linux私房菜》学习笔记(8)——bash脚本编程之变量
    《鸟哥的Linux私房菜》学习笔记(7)——grep及正则表达式
    《鸟哥的Linux私房菜》学习笔记(6)——管道及IO重定向
    《鸟哥的Linux私房菜》学习笔记(5)——权限管理
    《鸟哥的Linux私房菜》学习笔记(4)——用户和组
    《鸟哥的Linux私房菜》学习笔记(0)——磁盘与文件系统管理
    Intellij Idea 创建JavaWeb项目
  • 原文地址:https://www.cnblogs.com/LubinLew/p/Linux-System-API.html
Copyright © 2011-2022 走看看