天天看點

Linux C/C++ timeout指令實作(運作具有時間限制)

Linux附帶了大量指令,每個指令都是唯一的,并在特定情況下使用。Linux timeout指令的一個屬性是時間限制。可以為任何指令設定時間限制。如果時間到期,指令将停止執行。

如何使用timeout指令

我們将解釋如何使用Linux timeout指令

timeout [OPTION] DURATION COMMAND [ARG]…

timeout [OPTION]

DURATION可以是正整數或浮點數,後跟可選的機關字尾:

s - seconds (default)
m - minutes
h - hours
d - days
           

未使用機關時,預設為秒。如果持續時間設定為零,則禁用關聯的逾時。

其他選項

DESCRIPTION
       --preserve-status
              以與COMMAND相同的狀态退出,即使指令逾時

       --foreground
              當不直接從shell提示符運作逾時時,允許COMMAND從TTY讀取并獲得TTY信号;在此模式下,COMMAND的子級不會逾時

       -k, --kill-after=DURATION
              如果COMMAND仍在運作,也發送KILL信号在發出初始信号後很久

       -s, --signal=SIGNAL

              指定逾時時要發送的信号;SIGNAL可以是類似“HUP”的名稱或數字;有關信号清單,請參見“kill-l”

       --help
              顯示此幫助并退出 

       --version
              輸出版本資訊并退出

           

如何使用timeout指令的基本示例

  • 1.設定定時間後終止指令:
timeout 30 ping www.baidu.com

通過使用逾時,我們可以確定ping不會一直運作,占用網絡帶寬并糾纏任何正在ping的裝置。

Linux C/C++ timeout指令實作(運作具有時間限制)

此指令允許ping運作五秒鐘。它正在對www.baidu.com的域名進行ping,用于研究本文的測試網絡上。

如果程式的執行在逾時終止之前結束,逾時可以将退出代碼從程式傳遞回shell,要實作這一點,程式必須自動停止(換句話說,它不會因逾時而終止),并且必須使用–preserve-status選項。

如果使用值為5的-c(count)選項,ping将隻發出5個請求。如果我們給逾時一分鐘,ping肯定會自行終止。然後我們可以使用echo檢查退出值。

Linux C/C++ timeout指令實作(運作具有時間限制)
  • 2.發送正确的信号

當timeout想要停止程式時,它會發送SIGTERM信号。這禮貌地要求程式終止。某些程式可能選擇忽略SIGTERM信号。

我們可以通過請求逾時來發送SIGKILL信号來實作這一點。可以使用-s(signal)選項告訴逾時以發送SIGKILL信号。

timeout -s SIGKILL 20 sudo tcpdump -i ens33 -n -w 20230212.pcap
Linux C/C++ timeout指令實作(運作具有時間限制)

我們可以使用tcpdump 抓包的預設選項運作20秒後,發送SIGKILL信号終止程序。

  • 3.嘗試使用SIGTERM停止程式

我們使用-k(kill after)選項。-k選項需要一個時間值作為參數。在這個指令中,我們要求逾時,讓dmesg運作30秒,然後用SIGTERM信号終止它。如果dmesg在40秒後仍在運作,則意味着外交SIGTERM被忽略,逾時應發送SIGKILL以完成任務。

timeout -k 40 30 dmesg -w
Linux C/C++ timeout指令實作(運作具有時間限制)

dmesg運作30秒,并在收到SIGTERM信号時停止。

Linux C/C++ timeout指令實作

...
int main(int argc, char** argv) {
...
    for(int i=1;i<argc;i++) {
    	char* arg = argv[i];
    	if(strlen(arg) <= 0) continue;
    	
    	if(arg[0] == '-') {
			if(!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
				printHelp(argv[0]);
				return EXIT_SUCCESS;
			} else if(!strcmp(arg, "-9") || !strcmp(arg, "--kill")) {
				sig_kill = SIGKILL;
	...
			} else {
				fprintf(stderr, "Illegal argument: %s\n", arg);
				return EXIT_FAILURE;
			}
    	} else {
			/* 
			沒有更多選項。檢查是否有足夠的剩餘參數
			基本文法是/程式[OPTIONS]逾時程式[ARGS]
			而逾時和程式是強制性的  */
    		if (i+2 > argc) {		// 檢查是否給出逾時和程式
				fprintf(stderr, "Not enough arguments. Check %s --help, if you need help\n", argv[0]);
				if(i+1 > argc) {
					// 檢查參數是否為數字
					fprintf(stderr, "  Missing: TIMEOUT PROGRAM\n");
				} else {
					if(is_numeric(argv[i]))
						fprintf(stderr, "  Missing: PROGRAM\n");
					else
						fprintf(stderr, "  Missing: TIMEOUT\n");
				}
				return EXIT_FAILURE;
    		} else {
				int seconds = atoi(argv[i]);
				if(seconds < 0) {
					fprintf(stderr, "Timeout cannot be negative");
					return EXIT_FAILURE;
				}
				timeout = (unsigned int)seconds;
				
				// 合并程式和可選程式參數
				for(int j=i+1;j<argc;j++)
					command = strappend(command, argv[j]);
				
				break;
			}
    	}
    	
    }
    
    // 檢查程式參數
	if(command == NULL || strlen(command) <= 0) {
		fprintf(stderr, "Not enough arguments. Check %s --help, if you need help\n", argv[0]);
		fprintf(stderr, "  Missing: TIMEOUT PROGRAM\n");
		return EXIT_FAILURE;
	}
    
    // Fork守護程式(如果需要)
    if (daemonize) {
    	fork_daemon();
    }
    
    
    ...

		int status;
		pid_t wait_status;
		
		// 信号處理器
		signal(SIGINT, sig_handler);
		signal(SIGTERM, sig_handler);
		signal(SIGALRM, sig_handler);
		
		if(verbose) printf("Child process forked with pid %d.\n", proc_pid);
		
		// 設定報警
		if(timeout > 0) alarm(timeout);
		wait_status = waitpid(proc_pid, &status, 0);		// Wait for child
		runtime += millis();
		if(wait_status < 0) {
			fprintf(stderr, "Error waiting for process: %s\n", strerror(errno));
			return EXIT_FAILURE;
		}
		status = WEXITSTATUS(status);		// 擷取實際退出狀态
		if(status != 0) {
			if(verbose) fprintf(stderr, "Process exited with status %d after %ld milliseconds\n", status, runtime);
			return status;
		} else {
			if(verbose) printf("Process completed after %ld milliseconds\n", runtime);
			return status;
		}
	}
    
    ...
}
...
static void sig_handler(int sig_no) {
	switch(sig_no) {
		case SIGALRM:
			// Timeout
			if(verbose)
				printf("TIMEOUT after %ld milliseconds.\n", runtime+millis());
			else
				printf("TIMEOUT\n");
			terminate_process();
			exit(EXIT_FAILURE);
			break;
		case SIGINT:
		case SIGTERM:
			if(proc_pid <= 0) exit(EXIT_FAILURE);
			if(verbose) printf("Program termination request\n");
			if(proc_pid > 0) kill(proc_pid, sig_no);
			exit(EXIT_FAILURE);
			return;
	}
}

...
           

編譯運作

Linux C/C++ timeout指令實作(運作具有時間限制)

If you need the complete source code of timeout, please add WeChat number (c17865354792)​

總結

總結

timeout是一個指令行實用程式,它運作指定的指令,如果在給定的時間段後仍在運作,則終止該指令。

Welcome to follow WeChat official account【程式猿編碼】