摘要:
本文主要是关于system函数相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
点击查看本文更新记录
点击查看本文参考资料
点击查看相关文件下载
前边我们在《LV05-03-操作系统-进程操作》中已经学习过system函数的功能,这里我们再深入了解一下这个函数,因为我们实际工程中可能会经常使用这个函数。
一、system函数
1. system()
1.1函数说明
在 linux 下可以使用 man 3 system 命令查看该函数的帮助手册:
1 2
| #include <stdlib.h> int system(const char *command);
|
【函数说明】该函数可以在我们当前程序当中执行任意 shell 命令。system() 函数其内部的是通过调用 fork() 、 execl() 以及 waitpid() 这三个函数来实现它的功能。 system() 会调用 fork() 创建一个子进程来运行 shell (可以把这个子进程称为 shell 进程),并通过 shell 执行参数 command 所指定的命令。
【函数参数】
- command : const char * 类型,参数 command 指向需要执行的 shell 命令,以字符串的形式提供,如 “ls -al” 等。
【返回值】返回值为 int 类型,当参数 command 为 NULL ,如果 shell 可用则返回一个非 0 值,若不可用则返回 0 ;针对一些非 UNIX 系统,该系统上可能是没有 shell 的,这样就会导致 shell 不可能;如果 command 参数不为 NULL ,则返回值从以下的各种情况所决定:
- 如果无法创建子进程或无法获取子进程的终止状态,那么 system() 返回-1;
- 如果子进程不能执行 shell ,则 system() 的返回值就好像是子进程通过调用 _exit(127) 终止了;
- 如果所有的系统调用都成功, system() 函数会返回执行 command 的 shell 进程的终止状态。
【使用格式】一般情况下基本使用格式如下:
1 2 3 4 5
| #include <stdlib.h>
system("ps -l");
|
【注意】
system() 的主要优点在于使用上方便简单,编程时无需自己处理对 fork() 、 exec 函数、 waitpid() 以及 exit() 等调用细节, system() 内部会代为处理;当然这些优点通常是以牺牲效率为代价的,使用 system() 运行 shell 命令需要至少创建两个进程,一个进程用于运行 shell 、另外一个或多个进程则用于运行参数 command 中解析出来的命令,每一个命令都会调用一次 exec 函数来执行;所以从这里可以看出,使用 system() 函数其效率会降低,如果我们的程序对效率或速度有所要求,那么建议不要直接使用 system() 。
1.2使用实例
test.c1 2 3 4 5 6 7 8
| #include <stdio.h>
#include <stdlib.h> int main(int argc, char *argv[]) { system("ps -l"); return 0; }
|
在终端执行以下命令:
1 2
| gcc test.c -Wall # 生成可执行文件 a.out ./a.out # 执行程序
|
程序执行后,终端将会显示以下信息:
1 2 3 4 5
| F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 0 S 1000 11992 9420 0 80 0 - 5035 do_wai pts/1 00:00:00 bash 0 S 1000 17347 11992 0 80 0 - 595 do_wai pts/1 00:00:00 a.out 0 S 1000 17348 17347 0 80 0 - 658 do_wai pts/1 00:00:00 sh 0 R 1000 17350 17348 0 80 0 - 5254 - pts/1 00:00:00 ps
|
2. system()函数源码
上边的了解仅仅够我们了解到如何使用这个函数,以及这个函数有什么功能,那他内部是怎样的,使用过程中会有什么影响?我们先来看一下这个函数的源码,很遗憾,我没找到,它在 stdlib.h 文件中声明,但是我还是没有找到源码,就网上搜了一个,应该大差不差:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| int system(const char *cmdstring)
{ pid_t pid; int status;
if (cmdstring == NULL) { return (1); }
if ((pid = fork()) < 0) { status = -1; }
else if (pid == 0) {
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); -exit(127); } else { while (waitpid(pid, &status, 0) < 0) { if (errno != EINTER) { status = -1; break; } } } return status; }
|
从这里可以看出其实system函数调用之后会创建一个子进程,子进程调用execl去执行相应的shell命令,然后父进程会调用waitpid等待子进程退出,对子进城进程回收,这也就意味着,调用system的进程在这个时候会被阻塞,当执行的shell命令需要较长时间才能执行完毕的时候,调用system的进程就会等待很久的时间。
二、system耗时实例
1. debug_printf.h
点击查看详情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
|
#ifndef __DEBUG_PRINTF_H__ #define __DEBUG_PRINTF_H__
#include <stdio.h>
#define DEBUG_ENABLE 1 #define LOG_LEVEL DEBUG_ALL #define ERROR "ERROR" #define WARN "WARN " #define INFO "INFO "
#define CLS_ALL "\033[0m" #define F_BLACK "\033[30m" #define F_RED "\033[31m" #define F_GREEN "\033[32m" #define F_YELLOW "\033[33m" #define F_BLUE "\033[34m" #define F_PURPLE "\033[35m" #define F_CYAN "\033[36m" #define F_WHITE "\033[37m"
#define B_BLACK "\033[40m" #define B_RED "\033[41m" #define B_GREEN "\033[42m" #define B_YELLOW "\033[43m" #define B_BLUE "\033[44m" #define B_PURPLE "\033[45m" #define B_CYAN "\033[46m" #define B_WHITE "\033[47m"
#define BLACK_RED "\033[30;41m" #define BLACK_GREEN "\033[30;42m" #define BLACK_YELLOW "\033[30;43m"
enum DEBUG_LEVEL { DEBUG_OFF = 0, DEBUG_ERROR = 1, DEBUG_WARN = 2, DEBUG_INFO = 3, DEBUG_ALL = 4 };
#define MYDEBUG(TYPE, COLOR, FMT, ARGS...) \ do \ { \ if(DEBUG_ENABLE && (DEBUG_##TYPE <= LOG_LEVEL)) \ { \ printf(COLOR); \ printf("[%s][%s:%s:%d]"FMT, TYPE, __FILE__, __FUNCTION__, __LINE__, ##ARGS); \ printf(CLS_ALL"\n"); \ } \ } while(0)
#include <string.h> #include <time.h> #define filename(x) (strrchr(x,'/')?(strrchr(x,'/')+1):x) #define NORMAL_DEBUG 1
char *timeString(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); struct tm *timeinfo = localtime(&ts.tv_sec); static char timeString[30]; sprintf(timeString, "%.2d-%.2d %.2d:%.2d:%.2d", timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); return timeString; }
int sys_pts_get_ms(void) { struct timespec tv; long long lasttime = 0;
clock_gettime(CLOCK_MONOTONIC, &tv); lasttime = tv.tv_sec * 1000 + tv.tv_nsec / (1000 * 1000); return lasttime;
}
#define HKPRT(fmt...) \ do \ { \ if(NORMAL_DEBUG) \ { \ printf(F_YELLOW); \ printf("[%s][HK_LOG][%s:%d][%s]", timeString(), filename(__FILE__), __LINE__, __FUNCTION__); \ printf(CLS_ALL); \ printf(fmt); \ } \ } while(0) #define HKPRTE(fmt...) \ do \ { \ if(NORMAL_DEBUG) \ { \ printf(F_RED); \ printf("[%s][HK_LOG][%s:%d][%s]", timeString(), filename(__FILE__), __LINE__, __FUNCTION__); \ printf(CLS_ALL); \ printf(fmt); \ } \ } while(0)
#endif
|
2. system_called.c
点击查看详情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h> #include <unistd.h> #include "debug_printf.h" int main(int argc, char *argv[]) { int time0 = 0; int time1 = 0; time0 = sys_pts_get_ms(); HKPRTE("This is system_called!!!\n"); sleep(1); time1 = sys_pts_get_ms(); HKPRTE("system_called use time:%d\n", time1-time0); return 0; }
|
这个文件我们使用以下命令编译:
1
| gcc system_called.c -o system_called -Wall
|
然后便会生成 system_called 可执行文件,这个文件我们会在测试程序中通过system函数执行这个可执行文件。
3. test.c
点击查看详情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #include <stdio.h> #include <sys/shm.h> #include <sys/ipc.h> #include <string.h> #include <stdlib.h> #include <sys/prctl.h> #include <unistd.h>
#include <fcntl.h> #include <sys/stat.h> #include <semaphore.h> #include <pthread.h> #include <signal.h>
#include "debug_printf.h"
sem_t sem_sys;
void *thread(void *arg); void deleteSemfile(int sig); void sigintHandle(int sig);
int main(int argc, char *argv[]) {
pthread_t tid;
signal(SIGINT, deleteSemfile); signal(SIGQUIT, sigintHandle); sem_init(&sem_sys, 0, 0); pthread_create(&tid, NULL, thread, NULL); while(1) { sleep(1); }
return 0; }
void *thread(void *arg) { int time0 = 0; int time1 = 0; char * main_name = "thread"; prctl(PR_SET_NAME, (unsigned long)main_name); pthread_detach(pthread_self()); while(1) { sem_wait(&sem_sys); time0 = sys_pts_get_ms(); HKPRT("system call ./system_called\n"); system("./system_called"); time1 = sys_pts_get_ms(); HKPRT("system use time:%d\n", time1-time0); } pthread_exit("thread return!"); }
void sigintHandle(int sig) { static int count = 0; HKPRT("I catch the SIGQUIT![%d times] \n", ++count); sem_post(&sem_sys); }
void deleteSemfile(int sig) { sem_destroy(&sem_sys); HKPRT("Destroy sem_sys!\n"); exit(0); }
|
这是我们真正的测试函数,我们使用以下命令编译:
1
| gcc test.c -lpthread -Wall
|
这样我们便会生成一个 a.out 可执行文件,a.out可执行文件运行后,我们在终端按下 ctrl + \ 就可以产生一个 SIGQUIT 信号。
4. 测试结果
我们的上边的几个文件都放在同一目录下,然后我们执行a.out 可执行文件,再按下 ctrl+\ ,会有以下内容输出:
1 2 3 4 5 6
| hk@vm:~/6temp/test$ ./a.out ^\[01-15 15:18:19][HK_LOG][test.c:70][sigintHandle]I catch the SIGQUIT![1 times] [01-15 15:18:19][HK_LOG][test.c:59][thread]system call ./system_called [01-15 15:18:19][HK_LOG][system_called.c:10][main]This is system_called!!! [01-15 15:18:20][HK_LOG][system_called.c:13][main]system_called use time:1001 [01-15 15:18:20][HK_LOG][test.c:62][thread]system use time:1004
|
可以看到,我们的 a.out 中的 thread 线程执行一次一共耗时1004ms,而 system_called 可执行程序就耗费了1001ms,于是我们知道,system在某个线程中被调用的时候我们是要考虑到system执行的时间的。