자, 이전의 학습내용을 생각해보자.
- 쉘이란?
- 쉘에서 프로그램을 실행하는 방식
- execvp 함수를 사용해서 프로그램을 프로세스화하는법
그렇다면 이번 단원에서의 학습 목표는 무엇이었을까?
- 리눅스 쉘은 무엇을 하는가?
- 프로세스의 리눅스 모델
- 프로그램을 실행하는 방법 << 대충 이쯤까지 한거 아닐까?
- 프로세스를 생성하는 방법
- parent와 child 프로세스간의 소통방법
- for, exec, wait, exit 라는 System call과 Function에 대해 이해하기
- 명령어 sh, ps에 대해 알아보기
새로운 예시 코드가 주어졌다. 살펴보자
after.c
// after.c: execvp의 작업을 보여준다
#include <unistd.h>
#include <stdio.h>
int main(int argc, char* argv[]){
pid_t pid = getpid();
printf("After execvp(): %d\n", pid);
return 0;
}
이 코드를 살펴보자
이 두가지 기능을 갖고있다
이렇게 실행된다. execvp가 실행된 후의 프로세스 ID값을 출력해준다
before.c
//before.c: shows the same pid
// no matter how many times get executed.
#include <unistd.h>
#include <stdio.h>
//int main(int argc, char* argv[]){
void main(){
char* arglist[2];
pid_t pid = getpid();
arglist[0] = "./after";
arglist[1] = 0;
printf("Before execvp(): %d\n", pid);
execvp(arglist[0], arglist);
return;
}
자, 이번에는 before.c를 살펴보자
위 두 코드, 서로 연계되어있다.
before.c는 arglist 배열에 "./after"이 할당되어 있다.
즉, after.c를 실행파일로 지정하고, 이 실행파일을 실행시키는,
프로세스에 할당하는 그런 방식의 코드이다
결론적으로,
- pid를 따올수 있게됨
- 이전에 실행되던 프로세스와 동일한 pid를 가짐으로서 execvp 함수의 작동방식을 이해
이 두가지가 목적인 예시 코드들이다
//psh1.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#define MAXARGS 20
#define ARGLEN 100
int execute (char *arglist[]){
execvp(arglist[0]. arglist);
perror("execvp failed");
if(arglist != NULL){
free(arglist);
}
exit(1);
}
char* makestring(char *buf){
char *cp;
buf[strlen(buf)-1]='\0';
cp = malloc(strlen(buf)+1);
if( cp == NULL){
fprintf(stderr, "no memory\n");
exit(1);
}
strycpy(cp, buf);
return cp;
}
int main(int argc, char*argv[]){
char *arglist[MAXARGS+1];
int numargs;
char argbuf[ARGLEN];
char *makestring();
numargs = 0;
while(numargs < MAXARGS){
printf("Arg[%d]?", numargs);
if(fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n'){
arglist[numargs++] = makestring(argbuf);
}
else{
if(numargs > 0){
arglist[numargs] = NULL;
execute (arglist);
numargs = 0;
}
}
}
return 0;
}
자, 쉘이란 무엇인가?
- 기능에 대해 생각해보자,
- 프로그램 실행
- 입/출력 관리
- 사용자가 직접적으로 프로그래밍 가능한 ui
그러려면 어떻게 해야할지 생각해 보자
자, 일단 메인함수부터 살펴볼까?
int main(int argc, char*argv[]){ // 기본적인 메인함수선언, 명령행 인자와 데이터 전달
char *arglist[MAXARGS+1]; // arglist문자열 포인터를 선언한다, 길이는 MAXARGS+1
// 실행할 명령행 인수 저장
int numargs; // int형 변수 numargs선언, 현재까지 입력된 명령행 인수의 개수
char argbuf[ARGLEN]; // 사용자로부터 입력받은 명령행 인수를 임시로 저장
char *makestring(); // makestring() 함수의 프로토타입을 선언
numargs = 0; // numargs는 0으로 초기화됨
while(numargs < MAXARGS){ //MAXARGS값이 numargs값보다 클 경우 반복문이 실행된다
printf("Arg[%d]?", numargs); //numargs, 즉 입력된 명령행 인수의 인덱스를 출력
if(fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n'){ // fgets함수, 파일에서 문자열 한개씩
// 입력받는 함수, argbuf에 저장한다.
// 입력된 문자열이 '\n', 즉 엔터키가
// 눌리지 않으면 실행됨
arglist[numargs++] = makestring(argbuf); // makestring 함수를 사용,
// argbuf안에 저장된 문자열을 복사한다
// numargs 값 증가되면서 arglist 배열 하나씩 채움
}
else{ // 나머지 경우에는 어떨까?
if(numargs > 0){ // numargs값이 0보다 큰 경우?
arglist[numargs] = NULL; // arglist의 마지막 배열에 NULL포인터를 할당
// 인수 목록의 끝을 표시
execute (arglist); // execute 함수를 호출, 명령행 인수를 실행
numargs = 0; // numargs를 0으로 초기화,
//새로운 명령어를 받을 준비
}
}
}
return 0;
}
음... 전반적 기능을 살펴보니, 명령어를 문자열 방식으로 입력받아 실행시키는 기능을 하고 있다
쉘의 기능중 "프로그램 실행" 부분에 집중한 쉘이라고 볼 수 있을것같다여기서 쓰인, "makestring()" 함수가 무엇인지 봐야한다
※ fgets함수
- 함수 원형:
char* fgets(char* str, int num, FILE* pFile);
- 첫번째 인자: 파일에서 가져온 문자열을 넣는 변수, 문자열을 가리키는 char타입의 포인터
- 두번째 인자: 한번에 가지고 올 문자열의 길이를 넣는 변수
- 세번째 인자: 파일의 파일포인터를 집어넣음
- 가지고온 문자열 반환, or 파일의 끝일경우 NULL포인터 반환
자, 이제 makestring 함수를 찬찬히 살펴보자
char* makestring(char *buf){ // buf라는 이름의 문자열 버퍼를 입력받음,
// 동적으로 할당된 새로운 문자열 포인터 반환
char *cp; // 문자열 포인터 cp를 선언, 새로운 문자열 저장을 위한 포인터
buf[strlen(buf)-1]='\0'; //문자열의 마지막 문자를 NULL문자로 바꿈, '\n'을 제거하기 위함
cp = malloc(strlen(buf)+1); // 'strlen(buf)+1'만큼의 메모리를 동적으로 할당,
// strlen(buf)는 문자열의 길이, strlen(buf)+1은 널 문자까지 저장하기 위함
if( cp == NULL){ //cp값이 NULL이라면, 즉 동적 메모리 할당이 실패한경우
fprintf(stderr, "no memory\n"); //stderr스트림에 'no memmory' 를 출력함
exit(1); //프로그램 종료, 반환값으로 1 전달
}
strycpy(cp, buf); // 입력된 buf의 내용을 cp에 복사함
return cp; // cp값을 반환, 즉 새로 입력된 내용을 반환해줌
}
내용을 살펴봤다시피, 입력받은 문자열을 새로운 문자열인 cp에 저장하여 반환해주는 함수이다!
어떻게 보면, 기존 프로세스를 삭제하고 새 프로세스를 출력할때의 과정과 같다!
그럼 나머지 함수인 'execute()' 함수는 뭘까?
int execute (char *arglist[]){ // execute 함수의 선언, 문자열 포인터 배열 arglist를 입력받음
// int형 반환값을 가짐
execvp(arglist[0]. arglist); // excvp 함수를 호출, 명령행 인수 실행
perror("execvp failed"); // perror함수는 stderr스트림에 시스템 오류 메세지 출력
if(arglist != NULL){ // arglist 배열이 NULL이 아닌 경우
free(arglist); // arglist 배열을 해제
}
exit(1); // execute 함수의 실행오류로 종료
}
핵심은 몇가지 되지 않는다
- execvp 호출을 통해 프로그램 실행
- 에러 메세지 출력
간단하게 ls 명령어를 실행한거다