Skip to content
ChoYG edited this page Aug 2, 2016 · 1 revision

다른 프로그램 실행시키기

리눅스 환경에서 프로그램을 하나 만들다 보면 다른 프로그램을 실행시켜야 할 때가 있다. 예를 들어서 프로그램 내에서 'ps' 같은걸 실행해야 한다든지 말이다. 다른 사람들은 fork랑 exec를 비교하던데, 동작으로 봐서는 왜 비교가 되는지 나는 모르겠다 ㅡㅡ;

위 그림이 뭔 소리냐면, 원래 프로세스가 돌던중 exec 함수를 만나면 그자리에서 호출된 프로세스가 대신 돌아간다. 원래 프로세스가 돌다 만다는 의미. 뭐,,, 뒤에있는 예제를 보면 아마 느낌이 올꺼다.

exec 함수들

exec는 참 더럽게 함수가 많다. execl도 있고 execlp도 있고 뭐,,, 여기서는 딱 두가지 함수만 알려줄꺼다.
그렇게 많이 알아서 좋아지는것 같지도 않고,,,

exec 계열 함수에는 몇가지 규칙이 있다.

  • v : 배열 등을 이용하겠단 뜻
  • p : 사용자 PATH로 지정된 경로에서 검색하겠다는 뜻
  • e : 환경변수를 프로그램에서 설정할수 있다는 뜻

여기서는 e에 관한 함수는 다루지 않는다(내가 필요를 못느끼고 있으므로,,,).

execl

execl(경로, 명령, 값,,, NULL)

가변인자를 쓰는 함수다. execl을 사용할 경우에는 명령(프로그램)이 위치한 경로까지 같이 써줘야 한다. 예를들어 echo 명령어를 써서 hello를 출력시킨다면

execl("/bin/echo", "echo", "hello", NULL);

과 같이 써주면 된다. 이때 마지막에 NULL을 넣어줘야 함에 주의하자. 또한 execl을 호출한 시점에서 뒤에 나오는 내용은 실행되지 않는다는 점도 생각해두자.

/exec1/exec1.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
	printf("parent process start\n");

	if(fork() == 0)
	{
		execl("/bin/echo", "echo", "hello", NULL); //echo라는 외부 프로그램 실행
				//경로		명령	값		마지막NULL
		fprintf(stderr, "failed");
		exit(1);
	}
	
	sleep(1); //부모자식 끝나는 순서 맞추려고
	printf("parent process exit\n");
	return 0;
}
parent process start
hello
parent process exit

위 예제는 execl의 사용법과 특징을 단적으로 보여주고 있다. 자식 프로세스를 하나 만들어 echo명령어를 실행시키는 프로그램이다.
이때 눈여겨 봐야 할 점은 fprintf문과 exit는 실행되지 않는다는 점이다. execl이 정상적으로 동작하지 않는다면 fprintf문이 실행되겠지만, 정상적으로 실행된다면 실행된 프로세스가 원래 프로세스를 덮어써버릴 것이기 때문이다.

햇갈리는 점은 자식프로세스가 다른 프로세스로 덮어써져 버렸는데, wait와 waitpid 가 동작을 할 것인가이다. 결론적으로 동작한다. 왜냐하면 exec로 호출된 함수는 호출한 함수와 같은 pid를 갖기 때문에, 자식 프로세스가 호출했다면 자식 프로세스와 같은 pid를 갖기 때문이다.

/exec2/exec2.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
	printf("parent process start\n");
	
	if(fork() == 0)
	{
		execl("/bin/echo", "echo", "hello", NULL);
		fprintf(stderr, "first fail");
		sleep(1);
		exit(1);
	}

	if(fork() == 0)
	{
		execl("/bin/date", "date", NULL);
		fprintf(stderr, "second fail");
		sleep(2);
		exit(2);
	}

	if(fork() == 0)
	{
		execl("/bin/ls", "ls", "-l", NULL);
		fprintf(stderr, "third fail");
		sleep(3);
		exit(3);
	}

	sleep(6);

	printf("parent process finish\n");

	return 0;
}
parent process start
hello
2016. 08. 02. (화) 11:49:12 KST
합계 16
-rwxrwxr-x 1 mongcom mongcom 8817  7월 13 11:54 exec2
-rw-rw-r-- 1 mongcom mongcom  559  7월 13 11:54 exec2.c
parent process finish

이 예제에서는 부모 프로세스에서 자식 프로세스를 여러개 만들어 내 서로 다른 명령을 execl로 호출시킨다. 눈으로 보기 편하게 순서를 만들어주기 위해 sleep 함수를 사용하였다. 앞선 예제를 보았다면 execl 이후의 fprintf, sleep, exit 들은 실행이 되지 않을 것임을 예상할 수 있을 것이다.

execvp

execvp(명령, 명령어와 옵션 배열의 포인터);

execvp는 execl 과 비슷하면서 좀 다르다. 첫째로는 경로를 사용하지 않고 명령어만 써줘도 된다는 점이다. 왜냐하면 환경변수에서 명령어를 검색하는 과정까지 포함하기 때문이다. 그리고 그 뒤에는 명령어와 옵션이 저장된 배열의 포인터를 넘겨주면 된다. 말로 설명하니 언어에 이상이 생긴것 같다;

만약 execvp 라는 함수를 이용해 ps 와 옵션 -l 을 주어 실행시킨다면,

char* str[] = {"ps", "-l"}

execvp(str[1], &str[1]);
// 또는 execvp(str[1], str);

와 같이 사용하면 된다.

여타 exec 함수와 같이 함수가 호출된 시점에서 원래 프로그램의 뒷 내용은 실행되지 않는다.

/exec3/exec3.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>

int main(int argc, char *argv[])
{
	int child, pid, status;
	pid = fork();
	if(pid == 0)
	{
		execvp(argv[1], &argv[1]);
			//문자열	문자열들의 배열(첫번쨰 문자열의 포인터)
		fprintf(stderr, "%s:failed\n", argv[1]);
	}
	else
	{
		child = wait(&status);
		printf("[%d] child process %d finished", getpid(), child);
		printf("\t exit code %d \n", status >> 8);
	}
}
./exec3 ps -l  
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1000  4107  4096  0  80   0 - 12717 sigsus pts/3    00:00:00 zsh
0 S  1000  5172  4107  0  80   0 -  1049 wait   pts/3    00:00:00 exec3
4 R  1000  5173  5172  0  80   0 -  3859 -      pts/3    00:00:00 ps
[5172] child process 5173 finished	 exit code 0

ps -l을 인자로 주어 실행해보았다. execvp 뒤에 있는 fprintf 문은 역시 실행되지 않았으며, 실행된 ps는 자식프로세스와 같은 pid를 가져 wait 가 동작함을 확인할 수 있다.

Clone this wiki locally