본문 바로가기
Computer Science/UNIX & Linux

[UNIX/Linux] ep6) 프로세스 정보

by 클레어몬트 2024. 10. 12.

ㅁ디렉터리 트리의 산책

ㅇ ftw (test1.c): 주어진 디렉터리부터 출발하여 그 디렉터리 아래에 있는 모든 파일과 부 디렉터리에 대한 작업을 수행

int ftw(const char* path, int(*func)(), int depth)

 

int func(const char* name, const struct stat* sptr, int type)

- FTW_F : 객체가 하나의 파일임

- FTW_D : 객체가 하나의 디렉토리임

- FTW_DNR : 객체가 읽을 수 없는 하나의 디렉터리임

- FTW_SL : 객체가 하나의 심볼형 링크임

- FTW_NS : 객체가 심볼형 링크가 아니며, 따라서 stat 루틴이 성공적으로 수행될 수 없는 객체임

 

트리의 산책이 종료되는 경우

- leaf에 도달

- ftw에서 오류가 발생. 이때-1을 return

- 사용자가 정의한 함수가 0이 아닌 값을 돌려주면 종료됨. 이때 ftw는 수행을 그치고, 사용자 함수에 의해 복귀되었던 값을 return

 

 

https://claremont.tistory.com/entry/ep2-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4process

 

[운영체제] ep2) 프로세스(process)

ㅁ프로세스(process): 실행 중인 프로그램 (실행/스케줄링의 단위 및 자료구조)보조기억장치에 저장된 프로그램을 메모리에 적재하고 실행하는 순간, 그 프로그램은 프로세스가 된다그리고 이 과

claremont.tistory.com

ㅇ프로세스(process): 현재 실행 중인 프로그램

※프로그램: 사용자가 컴퓨터에 작업을 시키기 위한 명령어의 집합

 

현재 리눅스 시스템에서 실행 중인 프로세스를 확인하려면 ps, top 명령을 사용

ㅇps 명령: 프로세스 목록 보기

아무 옵션 없이 ps 명령을 사용하면 현재 터미널에서 실행한 프로세스들만 출력된다

 

ㅇtop 명령: 시스템 메모리 정보 보기

top 명령으로 확인할 수 있는 정보 중에서 메모리와 스왑 등에 관한 정보를 직접 검색하려면 sysinfo() 함수를 사용

 

 

시스템에서 프로세스를 식별하는 데는 PID(프로세스 ID)를 사용하고, 관련 프로세스들이 모여 프로세스 그룹을 구성

 ㅇ세션(session): POSIX 표준에서 제안한 개념으로, 사용자가 로그인해서 작업하는 터미널 단위로 프로세스 그룹을 묶은 것

 

 

ㅇ int sysinfo(struct sysinfo* info): 메모리와 스왑 상태 검색

- info : 검색 결과를 리턴하는 sysinfo 구조체의 주소

sysinfo 구조체에 검색 결과를 저장해 반환한다

성공하면 sysinfo 구조체에 정보를 저장한 후 0을 반환하고, 실패하면 –1을 반환

struct sysinfo {
	long uptime; // 시스템 부팅 후 경과된 시간을 초 단위로 저장
	unsigned long loads[3]; // 시스템 부하 평균을 저장하는 배열로 1분, 5분, 15분 기준으로 계산해 저장
	unsigned long totalram; // 사용 가능한 총 메모리 크기를 저장
	unsigned long freeram; // 사용 가능한 메모리의 크기를 저장
	unsigned long sharedram; // 공유 메모리의 크기를 저장
	unsigned long bufferram; // 버퍼가 사용하는 메모리의 크기를 저장
	unsigned long totalswap; // 스왑 영역의 총 크기를 저장
	unsigned long freeswap; // 사용 가능한 스왑 영역의 크기를 저장
	unsigned short procs; // 현재 실행 중인 프로세스 수를 저장
	unsigned long totalhigh; // 사용자에 할당된 메모리의 총 크기를 저장
	unsigned long freehigh; // 사용 가능한 사용자 메모리의 크기를 저장
	unsigned int mem_unit; // 메모리 크기를 바이트 단위로 저장
	char _f[20-2*sizeof(long)-sizeof(int)]; // 64바이트 크기를 맞추기 위한 패딩
};

 

#include <stdio.h>
#include <sys/sysinfo.h>

int main() {
    struct sysinfo info; // sysinfo 구조체 변수 info 선언

    sysinfo(&info); // 검색

    printf("Total Ram: %ld\n", info.totalram); // 시스템의 총 메모리 크기 출력
    printf("Free Ram: %ld\n", info.freeram); // 시스템에서 현재 사용 가능한 메모리의 크기를 출력
    printf("Num of Processes: %d\n", info.procs); // 현재 실행 중인 프로세스 수를 출력

    return 0;
}

시스템의 총 메모리는 약 4GB이고, 사용 가능한 메모리는 약 1GB, 프로세스는 총 505개임을 알 수 있음

 

 

PID는 0번부터 시작

0번 프로세스: 스케줄러로, 프로세스에 CPU 시간을 할당하는 역할 수행, 커널의 일부분이므로 별도의 실행 파일은 없음

1번 프로세스: init로 프로세스가 새로 생성될 때마다 기존 PID와 중복되지 않은 번호가 할당

 

ㅇ pid_t getpid(void): PID 검색

이 함수를 호출한 프로세스의 PID를 반환

 

 

※ PPID: 부모 프로세스의 PID

ㅇ pid_t getppid(void): PPID 검색

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("PID : %d\n", (int)getpid());
    printf("PPID : %d\n", (int)getppid());

    return 0;
}

 

ps 명령으로 이 터미널에서 실행 중인 프로세스를 보면 알 수 있다

 

 

(PGID 검색)

ㅇ pid_t getpgrp(void)

이 함수를 호출하는 프로세스가 속한 그룹의 PGID를 반환

 

ㅇ pid_t getpgid(pid_t pid)

- pid : PGID를 구하려는 프로세스의 ID

pid 인자로 지정한 프로세스가 속한 그룹의 PGID를 반환

(만일 인자가 0이면 getpgid() 함수를 호출한 프로세스의 PID를 반환)

 

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("PID : %d\n", (int)getpid());

    // 현재 프로세스의 PGID를 검색하는 첫 번째 방법
    printf("PGRP : %d\n", (int)getpgrp()); 
    // 현재 프로세스의 PGID를 검색하는 두 번째 방법
    printf("PGID(0) : %d\n", (int)getpgid(0));

    // PID가 18020인 프로세스가 속한 그룹의 PGID를 검색
    printf("PGID(18020) : %d\n", (int)getpgid(18020));

    return 0;
}

PID와 PGID가 같음을 알 수 있다. 이는 현재 프로세스가 단독 프로세스여서 프로세스 그룹에 홀로 속해 있기 때문! 파이프로 연결해 생성한 프로세스 그룹의 PGID를 검색한 결과는 18018로, 인자로 지정한 PID 18020과는 다른 프로세스임을 알 수 있음

 

 

ㅇ int setpgid(pid_t pid, pid_t pgid): PGID 변경

- pid : 프로세스 그룹에 속한 프로세스의 ID

- pgid : 새로 지정할 PGID

pid가 가리키는 프로세스의 PGID를 pgid로 지정한 값으로 지정

pid와 pgid가 같다면 pid에 해당하는 프로세스가 그룹 리더가 된다

(만약 pid가 0이면 이 함수를 호출하는 현재 프로세스의 PID를 사용)

(만약 pgid가 0이면 pid로 지정한 프로세스가 PGID)

 

 

 

ㅁ프로세스 실행 시간 - times() 함수를 이용

 

시간 정보를 이용해 프로세스 실행 시간을 측정할 수 있으며, 이것은 시스템 사용 요금을 결정하는 데 활용할 수 있다. 또한 프로그램에서 많은 시간을 소비하는 부분을 찾아 개선하는 데도 활용할 수가 있다.

 

[프로세스 실행 시간 = 시스템 실행 시간 + 사용자 실행 시간]

- 시스템 실행 시간: 프로세스에서 커널의 코드를 수행한 시간(시스템 호출로 소비한 시간)

- 사용자 실행 시간: 사용자 모드에서 프로세스를 실행한 시간(프로그램 내부의 함수나 반복문처럼 사용자가 작성한 코드를 실행하는 데 걸린 시간)

 

 

ㅇ clock_t times(struct tms* buf): 실행 시간 측정

- buf : 실행 시간을 저장할 tms 구조체의 주소

프로세스 실행 시간을 인자로 지정한 tms 구조체에 저장

임의의 시점으로부터 경과된 클록 틱 수를 반환, 실패하면 –1을 반환

times() 함수가 알려주는 시간 단위는 시계의 클록 틱

// tms 구조체는 sys/times.h 파일에 정의되어 있다
struct tms {
	clock_t tms_utime; // times() 함수를 호출한 프로세스가 사용한 사용자 모드 실행 시간
	clock_t tms_stime; // times() 함수를 호출한 프로세스가 사용한 시스템(커널) 모드 실행 시간
	clock_t tms_cutime; // times() 함수를 호출한 프로세스의 모든 자식 프로세스가 사용한 사용자 모드/실행 시간과 tms_utime의 합계 시간
	clock_t tms_cstime; // times() 함수를 호출한 프로세스의 모든 자식 프로세스가 사용한 시스템 모드/실행 시간과 tms_stime의 합계 시간
};

 

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

int main() {
    int i;
    time_t t;
    struct tms buf;
    clock_t ct, t1, t2;

    ct = sysconf(_SC_CLK_TCK);
    printf("Clock tick : %ld\n", ct);

    if ((t1 = times(&buf)) == -1) {
        perror("times 1");
        exit(1);
    }

    for (i = 0; i < 99999999; i++) {
    	time(&t);
    }
        
    sleep(1);

    if ((t2 = times(&buf)) == -1) {
        perror("times 2");
        exit(1);
    }
    
    printf("t1: %ld\n", t1);
    printf("t2: %ld\n", t2);
    printf("utime : %ld\n", buf.tms_utime);
    printf("stime : %ld\n", buf.tms_stime);
    printf("Real time : %.1f sec\n", (double)(t2 - t1) / ct);
    printf("User time : %.1f sec\n", (double)buf.tms_utime / ct);
    printf("System time : %.1f sec\n", (double)buf.tms_stime / ct);

    return 0;
}

 

 

 

 

환경 변수: 키-값 형태

환경 변수를 사용하면 프로세스 환경을 설정하거나 설정된 환경을 검색할 수 있다

환경 변수는 셸에서 값을 설정하거나 변경할 수 있으며 함수를 이용해 읽거나 설정할 수 있다

 

ㅇ env 명령: 현재 셸의 환경 설정들을 출력

환경 변수명은 관례적으로 대문자를 사용한다!

 

 

 

[전역 변수 사용] environ

#include <unistd.h>

extern char** environ;

 

전역 변수 environ은 환경 변수 전체에 대한 포인터이다

이 변수를 사용해 환경 변수를 검색할 수 있다

#include <stdio.h>
#include <unistd.h>

extern char** environ;

int main() {
    char** env;
    
    env = environ;
    while (*env) {
        printf("%s\n", *env);
        env++;
    }

    return 0;
}

 

 

[main 함수 인자 사용] int main(int argc, char* argv[], char* envp[])

Linux 에서는 환경 변수를 다음과 같이 main() 함수의 세 번째 인자로 지정해 사용할 수 있다

사용 방법은 전역 변수 environ과 같다

#include <stdio.h>

int main(int argc, char* argv[], char* envp[]) {
    char** env;

    env = envp;
    while (*env) {
        printf("%s\n", *env);
        env++;
    }

    return 0;
}

 

 

ㅇ char* getenv(const char* name): 환경 변수 검색

- name : 환경 변수명

인자로 지정한 환경 변수가 설정되어 있는지 검색해 결괏값을 저장하고 주소를 반환, 실패하면 NULL 포인터를 반환

#include <stdio.h>
#include <stdlib.h>

int main() {
    char* val;

    // getenv() 함수로 한경 변수 SHELL을 검색하고 결괏값을 문자형 포인터 val에 저장
    val = getenv("SHELL"); 
    if (val == NULL) {
        printf("SHELL not defined\n");
    } else {
        printf("SHELL = %s\n", val);
    }

    return 0;
}

SHELL의 값이 /bin/bash 임을 알 수 있다

 

 

ㅇ int putenv(char* string): 환경 변수 설정

- string : 설정할 환경 변수와 값으로 구성한 문자열

설정할 환경 변수를 ‘환경 변수명=값’ 형태로 지정하여 프로그램에서 환경 변수를 설정

putenv() 함수는 기존의 환경 변숫값은 변경하고, 새로운 환경 변수는 malloc()으로 메모리를 할당해 추가

공하면 0을 반환

#include <stdio.h>
#include <stdlib.h>

int main() {
    char* val;

    val = getenv("TERM");
    if (val == NULL) {
        printf("TERM not defined\n");
    } else {
        printf("1. TERM = %s\n", val);
    }

    putenv("TERM=vt100"); // TERM의 값을 vt100으로 변경

    val = getenv("TERM");
    printf("2. TERM = %s\n", val);

    return 0;
}

 

 

하지만 위 코드를 실행한 후 다시 env 명령으로 검색해 보면 TERM의 값이 코드를 실행하기 이전의 상태로 남아있는 것을 알 수 있다

 

 

ㅇ int setenv(const char* name, const char* value, int overwrite): 환경 변수 설정2

- name : 환경 변수명

- value : 환경 변숫값

- overwrite : 덮어쓰기

 putenv() 함수처럼 환경 변수를 설정하지만 다른 점은 변수와 환경 변숫값을 각각 인자로 지정한다

name에 지정한 환경 변수에 value의 값을 설정

overwrite는 name으로 지정한 환경 변수에 이미 값이 설정되어 있을 경우 덮어쓰기 여부를 지정

(overwrite 값이 0이 아니면 덮어쓰기를 하고 0이면 덮어쓰기를 하지 않는다)

#include <stdio.h>
#include <stdlib.h>

int main() {
    char* val;

    val = getenv("TERM");
    if (val == NULL) {
        printf("TERM not defined\n");
    } else {
        printf("1. TERM = %s\n", val);
    }

    setenv("TERM", "vt100", 0); // TERM의 값을 vt100으로 변경하고, overwrite 값을 0으로 지정
    val = getenv("TERM");
    printf("2. TERM = %s\n", val);

    setenv("TERM", "vt100", 1); // TERM의 값을 vt100으로 변경하고, overwrite 값은 1로 지정
    val = getenv("TERM");
    printf("3. TERM = %s\n", val);

    return 0;
}

overwrite 값이 0일 때는 TERM의 값이 변경되지 않고, overwrite의 값이 1일 때는 변경됨을 알 수 있음

 

ㅇ int unsetenv(const char* name): 환경 변수 설정 삭제

- name : 환경 변수명

name에 지정한 환경 변수를 삭제

(현재 환경에 name으로 지정한 환경 변수가 없으면 기존 환경을 변경하지 않는다)

 

 

 

 

 

 

 

참고 및 출처: 시스템 프로그래밍 리눅스&유닉스(이종원)