라이브러리 함수와 시스템 콜 차이

시스템 콜

커널 자원을 사용자가 사용할 수 있도록 미리 만들어 놓은 함수

사용자 모드 -> 시스템 콜 호출 -> 커널모드

라이브러리 함수

사용자가 많이 사용할 것 같은 기능을 미리 함수로 만들어 놓은 것

사용자 모드 -> 라이브러 함수 호출 -> 사용자 모드

차이가 멀까?

//오류 발생 이유: 메모리할당 하지 않음
time_t * cur_time;
 
time(cur_time);
 
printf("%d", (int)cur_time);

포인트 변수에 메모리를 할당하지 않고 사용하면 Segmentation Fault 오류 발생한다.

time_t * cur_time = (time_t *)malloc(sizeof(time_t));

시스템 콜은 커널이 건네주는 정보를 사용자 모드의 프로그램에 받아오려면 반드시 사용자 영역에 메모리를 할당해야 한다.

time_t * cur_time;
 
char * cur_string;
 
cur_string = ctime(cur_time);
printf("%s", cur_string);

하지만 라이브러리 함수는 사용자의 영역의 메모리를 별도로 할당하지 않아도 된다.

오류 처리 결과의 차이점도 있다.

시스템 콜 함수는 오류시 리턴 값은 -1이지만 라이브러리 함수는 오류시 NULL 값을 리턴한다.

'Operator System > Linux' 카테고리의 다른 글

쉘 프로그래밍 간단한 문법  (0) 2017.10.26
프로세스와 생성방법  (0) 2017.10.26
Make  (0) 2017.10.26
파일  (0) 2017.10.26
Compile과 라이브러리  (0) 2017.10.26

쉘이란?

shell은 사용자와 리눅스 사이의 인터페이스를 제공하는 프로그램이다.

리눅스 기본 쉘은 /bin/sh 에 존재한다.

로그인 쉘

사용자가 로그인할 때 실행되는 쉘로서 /etc/passwd 에 로그인 쉘 등록된다.

root:x:0:0:root:/root:/bin/bash

사용자명 : 패스워드 : 사용자 계정 uid : 사용자 계정 gid : 이름 : 홈 디렉토리 : 사용자 계쩡 로그인 쉘

변수

  • Linux 는 대소문자를 구분한다. 모든 변수는 String으로 표현한다.

  • 변수 값 정의는 "="을 사용하며 앞뒤에 공백문자 사용을 금지한다.

  • 변수 값 접근은 변수명 앞에 '$'을 사용한다.

  • 사용자 입력 값을 변수에 할당할 때는 read를 사용한다.

$ read n
5
$ echo $n
5

문법을 알아보자

if

#!/bin/sh
 
read timeofday
 
if [ $timeofday = "yes" ]
then
  echo "Good morning"
elif [ $timeofday = "no"]
then
  echo "Good afternoon"
else
  echo "so so"
fi
 
exit 0

for

#!/bin/sh
 
for foo in bar fud 43
do
  echo $foo
done
 
exit 0

while

#!/bin/sh
 
read trythis
 
while [ "$trythis" != "secret" ]
do
  echo "Sorry, try again"
  read trythis
done
 
exit 0

until

조건이 만족될 때까지 실행 금지. 적어도 한번은 실행

#!/bin/sh
 
until who | grep "$1" > /dev/null
do
  sleep 60
done
 
echo -e '\a'
echo "**** $1 has just logged in ****"
 
exit 0

case

#!/bin/sh
 
read timeofday
 
case "$timeofday" in
  yes) echo "Good morning";;
  no ) echo "Good Afternoon";;
  y ) echo "Good Morning";;
  n ) echo "Good Afternoon";;
  * ) echo "Sorry"
esac
 
exit 0

List

#!/bin/sh
 
touch file_one
 
if [ -f file_one ] && echo "hello"
then
  echo "in if"
else
  echo "in else"
fi
 
exit 0

출력화면

hello
in if

function

#!/bin/sh
 
foo() {
  echo "Function foo is executing"
}
 
echo "scriipt starting"
foo()
echo "script ended"
 
exit 0


'Operator System > Linux' 카테고리의 다른 글

라이브러리 함수와 시스템 콜 차이  (0) 2017.10.26
프로세스와 생성방법  (0) 2017.10.26
Make  (0) 2017.10.26
파일  (0) 2017.10.26
Compile과 라이브러리  (0) 2017.10.26

프로세스

프로세스 구성요소

  • 프로그램 코드

  • 데이터

  • 시스템 메모리를 사용하는 변수

  • 프로세스에서 오픈한 파일 디스크립터

  • 환경변수

프로세스 모델

같은 프로그램을 실행하는 서로 다른 프로세스

  • 프로그램 코드와 라이브러리 코드를 공유하기 때문에 하나의 프로그램에 대해 여러 프로세스가 공유

  • 프로세서 마다 고유 pid 할당

  • 프로그램 카운터 프로세스마다 고유

  • 데이터 공간과 스택공간은 프로세스마다 고유 할당

  • 프로세스가 open한 파일, 환경변수도 프로세스 별로 관리

task

리눅스에서 프로세스를 task라 정의, task_struct 라는 데이터 구조체를 사용하여 표현한다.

  • /usr/src/linux/include/linux/sched.h에 정의

  • 프로세스와 관련된 모든 정보를 정의한 데이터 구조체

  • 운영체제 버전이나 플랫폼에 따라 구조체 내부 변수들이 추가되거나 삭제

struct task_struct {
  volatile long state;
  ...
  pid_t pid;
  ...
}

프로세스 관련 소스 파일

  • kernel/sched.c : 프로세스 스케쥴러

  • kernel/signal : 시그널 처리 함수

  • kernel/fork.c : 프로세스 생성 함수

  • kernel/exit.c : 프로세스 종료 함수

  • fs/exec.c : 프로그램 실행

  • arch/i386/kernel/entry.S : 커널 초기 함수

  • arch/i386/kernel/process.c : architecture 의존 프로세스

PID와 프로세스 테이블

PID

각 프로세서는 프로세스 ID라는 유일한 식별자가 할당된다. 프로세스가 시작되면 할당되지 않은 다음 번호가 자동 할당된다.

프로세스 테이블

현재 메모리에 로딩된 모든 프로세스들에 대한 정보 저장하는 구조체

프로세스들에 대한 정보를 화면에 표시하기 위한 명령어 > ps -ef 또는 ps -ax

프로세스 스케쥴링

리눅스 커널에서는 다음 time slice에 실행할 프로세스를 선발 하기 위해 우선순위 기반의 스케쥴링을 사용한다.

프로세스의 우선 순위는 nice값에 의해 결정된다.

명령어설명
nicenice값을 10 증가
renicenice값을 변경
ps -lnice 값 확인

&을 이용하여 백그라운드 프로세스로 실행할 수 있다.

$ ./test &

프로세스 생성

system

"system" 라이브러리 함수를 사용하여 프로그램 내부에서 다른 프로그램을 실행하여 새로운 프로세스를 생성할 수 있다.

#include <stdlib.h>
int system(const char * string)

예시)

#include <stdlib.h>
#include <stdio.h>
 
int main() {
  printf("Running ps with system\n");
  system("ps ax");
  printf("Done\n");
  exit(0);
}

system 함수는 파라미터로 전달된 명령어 문자열을 실행한 후 종료될때까지 대기한다. 즉, "Running ps with system" 문자열 출력 후 ps ax 명령어를 실행 후 "Done"이라는 문자열이 출력된다.

fork()

현재 프로세스의 이미지 복사.

  • 현재 프로세스의 속성을 대부분 그대로 사용하는 새로운 프로세스를 생성해서 프로세스 테이블에 등록

  • 새로운 프로세스는 현재 프로세스와 동일한 코드를 사용하지만 고유 공간, 환경변수, 파일 디스크립터를 사용한다.

parent process는 wait(&status)를 호출하여 child process의 종료를 기다린다.

printf("pid = %d\n", getpid());
 
pid = fork();
 
switch(pid) {
  case -1:
    perror("fork error");
    exit(1);
  case 0:
    printf("child process\n")
    printf("getpid() = %d\n", getpid());
    printf("pid = %d\n", pid);
  default:
    printf("parent process\n");
    printf("getpid() = %d\n", getpid());
    printf("pid = %d\n", pid);
}
  • 위 코드를 실행시키면 fork함수 호출 전의 getpid() 값은 parent의 getpid()와 같음을 알 수 있다.

  • 하지만 fork() 함수의 반환값으로 얻은 pid는 부모 프로세스에게 새로운 pid를 할당한다. 해서 부모프로세스의 pid 값과 getpid() 값은 다르다는 것을 확인할 수 있다.

  • 자식 프로세스의 getpid() 값은 부모 프로세스의 pid와 같음을 확인할 수 있다. 자식 프로세스는 자신만의 pid를 갖게 되고, ppid는 부모 프로세스의 pid를 가지게 된다. PGID, SID 를 상속받으며, 파일지시자, 시그널등을 상속받는다. 단 파일잠금(lock)와 시그널 팬딩은 상속받지 않는다.

exec()

현재 프로세스의 이미지 변경.

  • fork()는 복사지만, exec()는 새로운 프로그램으로 다시 실행하게 된다.

  • 현재 프로세스가 더 이상 실행되지 않는다.("system"함수와 다름)

char * const ps_argv[] = {"ps", "ax", 0};
 
char * const ps_envp[] = {"PATH=/bin:/usr/bin", "TERM=console", 0};
 
execl("/bin/ps", "ps", "ax", 0);
execlp("ps", "ps", "ax", 0);
execle("/bin/ps", "px", "ax", 0, ps_envp);
execv("/bin/ps", ps_argv);
execvp("ps", ps_argv);
execve("/bin/ps", ps_argv, ps_envp);

위 코드는 아래의 exec 함수의 접미어에 따라 전달 방법을 달리한 예시이다.

  • l : 명령어 인자들을 리스트 형태로 전달

  • v : 명령어 인자들을 문자열 배열 형태로 전달

  • p : 명령어 파일명을 전달. 파일탐색경로는 PATH 환경변수 사용

  • e : 명렁어 실행에 사용할 환경변수 전달

예시)

#include <stdlib.h>
#include <stdio.h>
 
int main() {
  printf("Running ps with system\n");
  execlp("ps", "ps", "ax", 0);
  printf("Done\n");
  exit(0);
}

위 코드는 system 함수와 비교해서 보면, system 함수와 달리 execlp 함수가 호출됨으로 인해 프로세스 이미지가 변경되어 "Done"문자열을 출력하지 않고 ps ax 쉘을 실행시키고 종료한다.

프로세스 종료 대기

wait 함수는 자식 프로세스중 하나가 종료할 때까지 부모 프로세스를 대기시킨다.

  • 종료된 자식 프로세스의 pid를 반환

  • 종료된 자식 프로세스의 상태정보는 "stat_loc"에 저장.

#include <sys/types.h>
#include <sys/wait.h>
 
pid_t wait(int * stat_loc);
if(pid != 0) {
  int stat_val;
  pid_t child_pid;
 
  child_pid wait(&stat_val);
 
  printf("Child has finished: PID = %d\n", child_pid);
 
  if(WIFEXITED(stat_val))
    printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
  else
    printf("Child terminated abnormally\n");
 
  exit(exit_code);
}
매크로정의
WIFEXITED자식 프로세스가 정장석으로 종료하면 0이 아닌 값을 리턴
WEXITSTATUSWIFEXITED가 0이 아니면, child exit code 값을 리턴
WIFSIGNALED자식이 어떤 신호로 인해서 종료되었다면 0이 아닌 값을 리턴
WTERMSIGWIFSIGNALED가 0이 아니면, signal number 를 리턴
WIFSTOPPED자식 프로세스가 정지되었다면 0이 아닌 값을 리턴
WSTOPSIGWIFSTOPPED가 0이 아니면, signal number 를 리턴

좀비 프로세스

  • 부모 프로세스가 비정상 종료하면 운영체제에 의해 init 프로세스(PID = 1) 가 자식 프로세스들의 부모 프로세스가 된다.

  • 좀비 프로세스는 init 프로세스에 의해 제거될 때까지 프로세스 테이블에 남겨진다.


'Operator System > Linux' 카테고리의 다른 글

라이브러리 함수와 시스템 콜 차이  (0) 2017.10.26
쉘 프로그래밍 간단한 문법  (0) 2017.10.26
Make  (0) 2017.10.26
파일  (0) 2017.10.26
Compile과 라이브러리  (0) 2017.10.26

make

여러 개의 파일로 구성된 소프트웨어를 개발하는 도구

make 기능

  • 수정된 파일들을 필요에 따라 다시 컴파일

  • 소스 파일들의 컴파일 순서를 결정

  • 컴파일한 소프트웨어를 원하는 디렉토리에 설치

  • 메뉴얼 페이지를 작성

make 사용법

실행방법

  • Makefile이 있는 디렉토리에서 'make'를 실행

옵션

  • -k : 컴파일 과정에서 에러가 발생해도 무시하고 계속 진행

  • -n : 작업할 내용을 화면에 출력

  • -f : makefile 대신 사용할 파일을 지정

파라미터

  1. target

  • 타겟명이 파라미터로 지정되면 지정된 타겟을 생성한다.

  • 타겟 명이 지정되지 않으면 makefild에 나열된 타겟들 중에서 첫 번째 타겟을 생성한다.

  • 기본적으로 all 타겟을 첫 번째 타겟으로 설정하고 다른 타겟들은 all target을 만들기 위한 의존 타겟들로 정의 나열

  1. rule

  • 최종 응용 프로그램을 구성하는 각 파일들이 프로그램 소스파일들과 어떻게 관련되어 있는지를 설명

  1. prerequisites

  • 타겟을 만들 때 필요한 파일들의 리스트

  1. Recipe

  • prerequisites 를 사용하여 타겟을 만드는 방법을 기술

정의 방법

# simple Makefile
myapp: main.o 2.o 3.o
  gcc -o myapp main.o 2.o 3.o
 
main.o: main.c a.h
  gcc -c main.c
 
2.o: 2.c a.h b.h
  gcc -c 2.c
 
3.o: 3.c b.h c.h
  gcc -c 3.c

Macro

문법

  • MACRONAME = value,

사용법

  • (MACRONAME) or{MACRONAME}

Automatic Variable

  • $@ : 현재 타겟 이름

  • $* : 확장자를 제거한 타겟 이름

  • $< : 첫번째 prerequisites 이름

  • $^ : 전체 prerequisites 리스트

  • $? : 현재 타겟 생성 이후에 수정된 prerequisites 리스트

OBJS = hello.o hello_msg1 hello_msg2 hello_msg3
 
hello:$(OBJS)
  gcc -o $@ $(OBJS)
 
hello.o:hello.c
  gcc -c $<
 
hello_msg1.o:hello_msg1.c
  gcc -c $^[0]
 
hello_msg2.o:hello_msg2.c
  gcc -c $*.c

Internal Macro

명령어 앞에 추가되는 특수 문자

  • '-' : 명령어 실행시 발생하는 에러 무시

  • '@' : 명렁어 실행 전에 화면에 명령어를 출력하지 않음

Phony Target

  • Phony Target 이란 타게시름이 실제 파일이름이 아닌 타겟을 말한다.

  • Phony target 지정 방법은 명시적으로 특수 타겟인 ".PHONY"을 사용하여 명시적으로 지정한다.

사용 이유

  • 같은 이름의 파일과 충돌하는 것을 피하기 위한 목적

  • Make가 phony target을 위한 rule 탐색을 하지 않기에 성능 향상 목적

clean:
  -rm main.o 2.o 3.o

'-'를 이용하여 main.o 2.o 3.o를 에러를 무시하고 rm 을 수행

$ make clean

쉘에서 make clean을 주어 makefile 에 정의한 Phony Target을 지정

'Operator System > Linux' 카테고리의 다른 글

쉘 프로그래밍 간단한 문법  (0) 2017.10.26
프로세스와 생성방법  (0) 2017.10.26
파일  (0) 2017.10.26
Compile과 라이브러리  (0) 2017.10.26
환경변수  (0) 2017.10.26

파일

파일이란?

파일은 데이터를 저장하는 수단을 말하는 추상적인 개념.

파일 시스템

데이터 저장매체에 데이터를 저장하고 관리하는 방법.

디바이스 파일

디바이스 파일이란 입출력 디바이스를 표현하는 특수 파일로 /dev 디렉토리에 저장된다.

  • /dev/console : 시스템 콘솔

  • /dev/tty : 프로세스의 터미널 논리 디바이스

  • /dev/null : null 디바이스로 이 디바이스에 쓰여진 모든 데이터는 사라진다.

  • /dev/zero : 0으로 채워진 파일을 만들 때 사용

저수준 파일 함수

시스템 콜을 통한 접근으로 파일 디스크립터를 사용한다.

파일 디스크립터란 파일 접근에 사용되는 정수 값으로 0(입력), 1(출력), 2(에러)를 의미한다.

#include <fcntl.h>
#include <sys/types.h>
#incldue <sys/stat.h>
 
int open(const char * path, int oflags);
 
int open(const char * path, int oflags, mode_t mode);
  • oflags 는 O_RDONLY, O_WRONLY, O_CREAT등이 있다.

  • mode는 S_IRUSR, S_IWUSR 등이 있다.

#include <unistd.h>
 
int close(int fildes);
#include <unistd.h>
 
size_t read(int fildes, void * buf, size_t nbytes);
#include <unistd.h>
 
size_t write(int fildes, const void * buf, size_t nbytes);

고수준 파일 함수

표준 라이브러리 함수 호출을 통한 접근으로 파일 포인터(file stream 이용)를 사용한다.

#include <sys/stat.h>
 
int mkdir(const char * path, mode_t mode);

디렉토리 생성

#include <unistd.h>
int rmdir(const char * path);

디렉토리 삭제

#include <unistd.h>
int chdir(const char * path);

디렉토리 변경

#include <unistd.h>
char * getcwd(char * buf, size_t size);

현재 디렉토리명 반환

#include <sys/types.h>
#include <dirent.h>
 
DIR * opendir(const char * name);

디렉토리 스트림 생성

 
#include <sys/types.h>
#include <dirent.h>
 
struct dirent * readdir(DIR * dirp);

디렉토리 읽기

#include <sys/types.h>
#include <dirent.h>
 
vodi seekdir(DIR * dirp, long int loc);

디렉토리 엔트리 포인트를 loc 위치로 이동

#include <sys/types/h>
#include <dirent.h>
 
int closedir(DIR * dirp);

디렉토리 스트림 해제

파일 및 디렉토리 판별

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
 
void printdir(char * dir, int depth) {
  DIR * dp;
  struct dirent * entry;
  struct stat statbuf;
 
  if((dp = opendir(dir)) == NULL) {
    fprintf(stderr, "cannot open directory: %s\n", dir);
  }
 
  chdir(dir);
 
  while((entry = readdir(dp)) != NULL) {
    lstat(entry->d_name, &statbuf);
    if(S_ISDIR(statbuf.st_mode)) {
      if(strcmp(".", entry->d_name) == 0 ||
        strcmp("..", entry->d_name) == 0) continue;
 
      printf("%*s%s\n", depth, "", entry->d_name);
 
      printdir(entry->d_name, depth+4);
    } else {
      printf("%*s%s\n", depth, "", entry->d_name);
    }
  }
 
  chdir("..");
  closedir(dp);
}

설명 :

opendir을 하여 읽기에 성공하였으면 dp NULL이 아닌 값을, 실패하면 NULL 값을 얻는다.

dp에는 해당 디렉토리에 대한 스트림에 대한 정보가 들어있다.

readdir(dp)를 통해 해당 디렉토리에 대한 파일정보를 struct dirent * 구조체인 entry에 넣는다.

lstat은 링크에 대한 정보를 얻는 함수로 entry->d_name이 가리키는 파일 정보를 문자열 배열에다가 저장한다.

S_ISDIR(statbuf.st_mode) 함수로 디렉토리 여부 검사를 한다.

procfs

디바이스 드라이버나 운영체제 커널에 대한 실시간 정보를 제공해주는 특수 파일 시스템이다.

'Operator System > Linux' 카테고리의 다른 글

쉘 프로그래밍 간단한 문법  (0) 2017.10.26
프로세스와 생성방법  (0) 2017.10.26
Make  (0) 2017.10.26
Compile과 라이브러리  (0) 2017.10.26
환경변수  (0) 2017.10.26

컴파일이란?

컴파일 과정

소스파일 + 헤더파일 -> 중간파일(전처리) -> 오브젝트파일(컴파일) -> 실행파일(링킹)

컴파일 옵션

  • I : 헤더파일 위치 지정 옵션, 표준 디렉토리(/usr/include)외에 있는 헤더파일의 디렉토리 경로 지정

  • Dname : name에 지정된 심볼을 1로 정의

  • ON : 컴파일된 코드를 최적화 시키는 옵션

  • L : 라이브러리 파일 이 존재하는 디렉토리 경로 지정

  • l string : 옵션 뒤에 지정한 라이브러리 파일을 지정하여 컴파일 시 링크(단 파일명은 libstring.a 또는 libstring.so)

  • c : 컴파일 단계까지만 수행하고 링크 수행 x 하여 오브젝트파일을 생성

  • g : 실행파일에 표준 디버깅 정보 포함하여 gdb, DDD 등 디비거 이용시 옵션 사용

  • Wall : gcc가 제공하는 모든 경고를 알 수 있다. 시스템 혹은 커널 프로그램 작성 시 이 옵션을 함께 넣어 컴파일 하는 것이 적절.

컴파일 옵션을 이용하여 라이브러리 파일을 링크

#include <stdio.h>
#include <math.h>
 
int main() {
  double dnum = 100.0;
 
  printf("%lf", sqrt(dnum));
 
  return 0;
}

위 코드의 sqrt는 표준 C 라이브러리가 아니라 별도의 math 라이브러리 파일에 들어가 있기 때문에 컴파일 시 함께 링크될 수 있도록 옵션을 추가 해야 한다.

수학 함수 이용시

라이브러리 파일명파일 위치컴파일 시 필요 옵션
libm.a 또는 libm.so/usr/lib 와 /lib-lm
gcc -o test test.c -lm

라이브러리 생성

라이브러리 생성을 하게 되면 동일 기능이 여러 군데 중복되지 않아 효율적인 프로그램 작성을 할 수 있다.

생성 형태

C 컴파일러는 컴파일 시 기본적으로 /usr/lib에 있는 디렉토리에서 라이브러리 함수를 찾아 링크한다.

만약 없다면 -l 혹은 -L 옵션을 이용하여 다른 디렉토리에 있는 라이브러리도 이용 가능하다.

라이브러리의 이름은 lib로 시작하여 .a 나 .so 의 확장자로 구성된다.

정적 라이브러리

프로그램 오브젝트 파일 + 미리 생성한 라이브러리 함수 오브젝트 코드 = 실행 파일

정적 라이브러리 함수를 사용하게 되면 코드 중복을 막을수는 있지만 메모리에 동일한 코드가 존재하게 되어 메모리, 디스크 등이 자원을 효율적으로 사용하지 못하는 단점이 있다.

라이브러리 생성 방법

  1. 소스 파일 생성 vi static.c

  2. 오브젝트 파일 생성(static.o) gcc -c static.c

  3. 라이브러리 파일 생성 ar rv libstatic.a static.o -> ar 도구를 이용하여 static.o 오브젝트를 libstatic.a 라는 정적 라이브러리 파일로 생성

공유 라이브러리

프로그램 오브젝트 코드 + 라이브러리 함수 정보 = 실행 파일

라이브러리 생성 방법

  1. 소스 파일 생성 vi shared.c

  2. 오브젝트 파일 생성 gcc -c -fPIC shared.c -> -c 옵션과 -fPIC 옵션을 이용하여 재배치 가능한 오브젝트 파일 생성

  3. 라이브러리 파일 생성 ld -shared -o libshared.so shared.o -> ld 도구를 이용하여 shared.o 오브젝트를 libshared.so 공유 라이브러리 파일로 생성

라이브러리 작성 예시

//static.c
#include <stdio.h>
 
void static_function(int num) {
    printf("this is static function : %d\n", num);
}
//shared.c
#include <stdio.h>
 
void shared_function(int num1, num2) {
  printf("this is shared function : %d %d\n", num1, num2);
}
//static.h
void static_function(int);
//shared.h
void shared_function(int, int);
//main.c
#include <stdio.h>
#include "static.h"
#include "shared.h"
 
int main() {
    printf("main start.\n");
    static_function(100);
    shared_function(200, 300);
    printf("main end.\n");
    return 0;
}

필요한 파일들을 작성합니다.

$ gcc -c static.c
$ ar rv libstatic.a static.o

정적 라이브러리 파일을 생성합니다.

$ gcc -c -fPIC shared.c
$ ld -shared -o libshared.so shared.o

동적 라이브러리 파일을 생성합니다.

 
$ gcc -o main main.c -L. -lstatic -lshared
 

gcc 옵션을 통해 현재 디렉토리 경로를 지정하고 해당 라이브러리 파일들을 지정합니다.

 
$ ./main
 

실행파일 실행 시 공유 라이브러리를 찾을 수 없다는 오류가 뜨게 됩니다.

 
$ export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:.
 
$ ./main


'Operator System > Linux' 카테고리의 다른 글

쉘 프로그래밍 간단한 문법  (0) 2017.10.26
프로세스와 생성방법  (0) 2017.10.26
Make  (0) 2017.10.26
파일  (0) 2017.10.26
환경변수  (0) 2017.10.26

환경변수

환경변수는 쉘 스크립트나 프로그램 실행을 제어, 실행환경 설정하는데 사용된다.

지역 환경 변수

현재 쉘에만 적용되는 변수, 환경변수명 = 변수

전역 환경 변수

현재 쉘과 자식 쉘에 같이 적용되는 변수, exports 환경변수명 = 변수

환경 변수 해제

unset

환경 변수 확인법

echo, set, env

환경 변수 파일 적용

$ source test_sh_var

대표적인 환경변수로 아래와 같다.

  • HOME : 사용자 홈 디렉토리

  • PATH : 실행파일 탐색 경로

  • LANG : 기본 언어

  • PWD : 사용자 현재 작업 디렉토리

  • TERM : 터미널 타입

  • SHELL : 로그인 쉘

  • BASH : bash 쉘 경로

  • PS1, PS2 : shell prompt 변수

대표적인 환경 변수

환경변수설명
HOME,PATH홈, 경로
$PS1command prompt
$PS2추가적인 입력을 받을 때 사용하는 prompt
$IFSinput field seperator.
$#쉘 스크립트에 전달되는 파라미터 수
$0쉘 스크립트 이름
1,2, $3첫번째, 두번째, 세번째 파라미터
$$쉘 스크립트의 Process Id
$*스크립트의 모든 파라미터를 1개 변수로 표현
$@IFS를 사용하지 않고 $*로 출력한 것
$ IFS=''
$ set foo bar bam
$ echo "$@"
foo bar bam
$ echo "$*"
foobarbam
$ unset IFS
$ echo "$*"
foo bar bam

IFS를 활용한 예제이다.

getenv(), putenv(), environ 변수

#include <stdlib.h>
 
char * getenv(const char * name);
int putenv(const char * string);
 
#include <stdlib.h>
extern char ** environ;

getenv는 환경변수에 설정된 값을 반환한다.

putenv는 "변수명=변수값" 형태의 문자열을 받아 생성한다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, int * argv[]) {
  char * var, * value;
 
  if(argc == 1 || argc > 3) {
    fprintf(stderr, "usage: environ var [value]\n");
    exit(1);
  }
 
  var = argv[1];
  value = getenv(argv[1]);
 
  if(value) {
    printf("Variable %s has value %s\n", var, value);
  }
 
  if(argc == 3) {
    char * string;
    value = argv[2];
    string = malloc(strlen(var) + strlen(value) + 2);
 
    if(!string) {
      fprintf(stderr, "out of memory\n"); exit(1);
    }
 
    strcpy(string, var);
    strcat(string, "=");
    strcat(string, value);
 
    printf("Calling putenv with : %s\n", string);
    if(putenv(string) != 0) {
      fprintf(stderr, "put env failed\n"); free(string); exit(1);
    }
    value = getenv(var);
    if(value) printf("New value of %s is %s\n", var, value);
  }
 
  exit(0);
}

putenv를 활용하기 위해 배열에 "변수명=변수값" 형식으로 저장함을 확인할 수 있다.

#include <stdio.h>
#include <stdlib.h>
 
int main() {
  char * homedir, filename[80];
  FILE * fp;
 
  homedir = getenv("HOME");
  strcpy(filename, homedir);
  strcat(filename, "/test.log");
 
  if((fp = fopen(filename, "w")) == NULL) {
    perror("fopen");
    exit(1);
  }
 
  fwrite("getenv test\n", 12, 1, fp);
  fclose(fp);
 
  return 0;
}

위 예제는 getenv를 통해 HOME 환경변수에 대한 정보를 얻어 경로 조작 후 파일을 생성하는 예제이다.

#include <stdlib.h>
#include <stdio.h>
 
extern char ** environ;
 
int main() {
  char ** env = environ;
 
  while(*env) {
    printf("%s\n", *env);
    env++;
  }
 
  exit(0);
}

environ 변수를 사용해 환경변수와 그 설정값들을 ""변수=값" 형태의 문자열로 저장된 문자열 배열을 가져온다. extern을 사용해 외부 파일에서 참조가 가능하게 해줘야 한다. 실행 결과는 env 명령어를 준 결과와 동일하게 나타난다.

'Operator System > Linux' 카테고리의 다른 글

쉘 프로그래밍 간단한 문법  (0) 2017.10.26
프로세스와 생성방법  (0) 2017.10.26
Make  (0) 2017.10.26
파일  (0) 2017.10.26
Compile과 라이브러리  (0) 2017.10.26

+ Recent posts