728x90

시스템 프로그래밍 6주차에는 프로세스 정보에 관하여 배운다.

 

  • 프로세스란?

일단 프로그램과 프로세스의 차이에 대하여 먼저 알아보자.

프로그램은 실행 할 프로그램 과 데이터를 합친 컴퓨터 시스템에 실행 요청 전의 상태이다.

프로세스는 실행을 위해 커널에 등록된 작업을 말한다.

 

메모리 안에서 프로세스는

이렇게 차지하게 된다.

 

이제 리눅스 시스템에서 현재 실행중인 process 정보를 확인해보자.

윈도우의 작업관리자라고 생각하면 될 것이다.

ps를 사용하면 현재 실행중인 process 정보를 확인 할 수 있다.

옵션을 추가하자면 -j를 추가하면 job control format으로 출력이 가능하다.

 

top을 이용하면 현재 시스템 내 process 정보를 실시간 확인 가능하다.

사실 이 명령어가 ps 보다 더 작업관리자에 가깝다.

 

kill -signal pid 를 이용하면 해당 process에 signal을 전달한다.

이 부분은 나중에 더 자세히 배워보도록 하자.

 

  • 프로세스 계층

Process ID

Process에 부여된 식별 번호로 시스템 내 유일한 번호가 할당이 된다.

 

Parent process

자신을 생성한 프로세스이며, 모든 프로세스는 부모 프로세스가 있다.

최상위 process = kernel (pid = 0)

Parent process의 ID를 PPID라고 한다.

 

Getting PID/PPID

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

pid_t getpid(void);
pid_t getppid(void);

parameter는 없고 Process ID와 Parent process ID를 반환한다.

 

바로 예제 코드를 작성해보자

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

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

	return 0;
}

그냥 단순히 ID들을 가져오며 실행을 해보면

Process group

관련 process들을 묶은 것을 process group이라고 한다.

하나의 job 수행을 목적으로 하며, Process group에 전달 된 signal은 그룹 내 모든 프로세스에게 전달 된다.

 

그리고 그 중 가장 중심이 되는 Process를 Process group leader라고 한다.

Process Group의 ID는 leader의 PID를 따라간다.

 

Process group을 확인해보자.

우선 shell script를 작성한다.

echo test.sh ... start
ping 192.168.0.1
echo test.sh ... end

실행을 하고 터미널을 하나 더 열어서 Process ID들을 확인해본다.

이 화면이 Shell Script를 실행한 화면이고

ps -j e 명령어를 사용한 화면이다.

PGID가 같은 것들이 있는 것을 확인 할 수 있다.

 

kill -[signal num] -[groupID]로 해당 그룹 전체에 신호를 보낼 수 있다.

이 부분은 다음 시간에...

 

Setting process group ID

#include <unistd.h>

int setpgid(pid_t pid, pit_t pgid);

-pid

옮기고 싶은 target process의 pid

-pgid

설정하려는 group의 pgid

 

pid를 0으로 주면 current process로 설정하고, pgid = 0이면 pgid를 pid로 설정한다.

 

근데 여기서, 아무 group으로 이동할 수는 없고 같은 session 내에서만 이동이 가능하다.

 

session은 사용자가 로그인해 작업하고 있는 터미널 단위로 프로세스 그룹을 묶은 것을 말한다.

session은 하나의 foreground process group과 0개 이상의 background process group을 가진다.

foreground process group은 사용자의 입력을 받는 process group이고

background process group은 foreground process group 외의 process group을 말한다.

 

session에도 leader가 있는데 login shell process이다.

 

  • Process 실행 시간

Process running time = system running time + user running time 이며

 

직관적으로 알 수 있겠지만

System running time은 System call에 소요된 시간

User running time은 사용자 모드에서 수행한 시간으로 사용자 작성 코드를 실행하는데 걸린 시간이다.

 

Getting process running time

#include <sys/times.h>

clock_t times(struct tms *buf);

-buf

running time 정보를 저장할 tms 구조체의 포인터이다.

return: 특정 시점부터 경과한 시간, -1(error)

 

parameter로 들어가는 tms 구조체는

struct tms {
	clock_t tms_utime;      /* [XSI] User CPU time */
	clock_t tms_stime;      /* [XSI] System CPU time */
	clock_t tms_cutime;     /* [XSI] Terminated children user CPU time */
	clock_t tms_cstime;     /* [XSI] Terminated children System CPU time */
};

이렇게 구성이 되어 있다.

 

바로 코드를 작성해보자.

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

int main(void){
	time_t t;
	struct tms mytms;
	clock_t t1, t2;

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

	sleep(5);

	for(int i = 0; i < 999999; i++) time(&t);

	if((t2 = times(&mytms)) == -1) {perror("times 2"); exit(1);}

	printf("Read time : %.1f sec\n", (double)(t2 - t1) / sysconf(_SC_CLK_TCK));
	printf("User time : %.1f sec\n", (double)mytms.tms_utime / sysconf(_SC_CLK_TCK));
	printf("System time : %.1f sec\n", (double)mytms.tms_stime / sysconf(_SC_CLK_TCK));

	return 0;
}

이렇게 실행시간을 측정해보면

User time과 System time까지 구할 수 있다.

 

  • 환경변수

환경 변수는 Process 실행환경에 정의되어 있는 변수를 말한다.

우선 shell 환경에서 먼저 보자면

 

env를 사용해서 환경변수 목록을 확인할 수 있다.

$export 를 사용해서 환경변수를 선언할 수 있다. ($export 환경변수명 = 값)

Getting enviroment variables

프로그램을 만들 때에도 환경 변수를 가져올 수 있다.

 

environ 전역 변수 사용

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

extern char **environ;

int main(void){
	char **env;

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

	return 0;
}

 

main의 인자로 전달

#include <stdio.h>

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

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

	return 0;
}

 

#include <stdlib.h>

char *getenv(const char *name);

-name

읽어올 환경변수 명

return: 환경변수 값, NULL(error)

 

이렇게 원하는 환경변수만을 가져올 수 있다.

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

int main(void){
	char *val;

	val = getenv("MY_NAME");
	if(val == NULL) printf("MY_NAME not defined\n");
	else printf("MY_NAME = %s\n", val);

	return 0;
}

 

Setting enviroment variables

#include <stdlib.h>

int putenv(char *string);
int setenv(const char *name, const char *value, int overwrite);

-string

추가할 환경변수와 값 (변수=값)

-name

추가할 환경변수의 이름

-value

추가할 환경변수의 값

-overwrite

추가되는 인수

 

Process에 의해 추가되는 환경 변수는 자신과 자식 프로세스에서만 유효하다.

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

int main(void){
	char *val;

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

	putenv("MY_NAME=SK");

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

	return 0;
}

이렇게 MY_NAME에 해당하는 환경변수를 변경해도

그 process에서만 유효하다.

 

Unsetting enviroment variables

#include <stdlib.h>

int unsetenv(const char *name);

-name

삭제할 환경변수

return: 0(success), -1(error)

 

직관적으로 알 수 있듯 환경변수를 삭제하는 system call이다.

728x90

시스템프로그래밍 5주차에는 시스템 정보에 관하여 배운다.

 

MAC에서는 사용자 전환이 잘 안되어서... groom으로 진행하겠습니다.

 

  • 시스템 정보

시스템에 설치된 운영체제에 관한 정보, 호스트명 정보, 하드웨어 종류에 대한 정보 등을 얻는 방법을 알아보자.

 

uname

shell에 uname을 사용하면 시스템의 기본 정보를 출력한다.

system call로 알아보자

#include <sys/utsname.h>

int uname(struct utsname *buf);

-buf

읽은 정보를 저장할 utsname 구조체의 포인터

return: 0(success), -1(error)

 

여기에 들어가는 utsname 구조체에 대해 살펴보면

struct  utsname {
	char    sysname[_SYS_NAMELEN];  /* [XSI] Name of OS */
	char    nodename[_SYS_NAMELEN]; /* [XSI] Name of this network node */
	char    release[_SYS_NAMELEN];  /* [XSI] Release level */
	char    version[_SYS_NAMELEN];  /* [XSI] Version level */
	char    machine[_SYS_NAMELEN];  /* [XSI] Hardware type */
};

이고 이 구조체를 이용하여 시스템의 기본 정보를 가져오는 코드를 작성해보자.

#include <sys/utsname.h>
#include <stdlib.h>
#include <stdio.h>

int main(void){
	struct utsname uts;

	if(uname(&uts) == -1){
		perror("uname");
		exit(1);
	}

	printf("OSname : %s\n", uts.sysname);
	printf("Nodename: %s\n", uts.nodename);
	printf("Release: %s\n", uts.release);
	printf("Version: %s\n", uts.version);
	printf("Machine: %s\n", uts.machine);
	
	return 0;
}

sysinfo

sysinfo를 사용해서 다른 정보들을 가져올 수도 있다.

#include <sys/sysinfo.h>

int sysinfo(struct sysinfo *info);

-info

읽은 정보를 저장할 sysinfo 구조체의 포인터

return: 0(success), -1(error)

 

sysconf

#include <unistd.h>

long sysconf(int name);

-name

검색할 정보를 지칭하는 상수

return: 요청한 정보의 값, -1(error)

 

이번엔 리소스의 정보를 가져오는 system call이다.

 

상수의 예

상수 설명
_SC_ARG_MAX(1) argv[]와 envp[]를 합한 최대 크기로, 바이트 단위로 표시한다.
_SC_CHILD_MAX(2) 한 UID에 허용되는 최대 프로세스 개수를 나타낸다.
_SC_CLK_TCK(3) 초당 클록 틱 수를 나타낸다.
_SC_OPEN_MAX(5) 프로세스당 열 수 있는 최대 파일 개수를 나타낸다.
_SC_VERSION(8) 시스템이 지원하는 POSIX.1의 버전을 나타낸다.
_SC_PASS_MAX(9) 패스워드의 최대 길이를 나타낸다.
_SC_LOGNAME_MAX(10) 로그인명의 최대 길이를 나타낸다.
_SC_PAGESIZE(11) 시스템 메모리의 페이지 크기를 나타낸다.

sysconf를 이용해서 리소스의 정보를 출력해보자면

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

int main(void){
	printf("Clock Tick : %ld\n", sysconf(_SC_CLK_TCK));
	printf("Max Open File : %ld\n", sysconf(_SC_OPEN_MAX));
	printf("Max Login Name Length : %ld\n", sysconf(_SC_LOGIN_NAME_MAX));
	
	return 0;
}

  • 사용자 정보

사용자를 추가하거나 권한을 관리하는 방법들이다.

우선 shell에서 사용자(seungkyu)를 추가해보자.

sudo adduser [사용자 이름]을 입력하면 된다.

일단 이렇게 모두 디폴트로 설정을 하면 된다.

 

해당 사용자로 접근을 해보자.

su [사용자 이름] 명령어를 사용하면 해당 사용자로 접근할 수 있다.

exit로 나갈 수 있다.

UID는 그 사용자에게 부여된 ID 번호이며, Login name은 문자 형태의 사용자 이름이다.

이렇게 id [사용자 이름]으로 확인 할 수 있다.

Process에 입장 할 때의 사용자에 대해 알아보자.

- Real user ID

최초에 Process를 실행한 user의 UID

- Effective user ID

현재 Process가 행사하는 UID

- Saved user ID

Process의 최초의 effective user ID

 

Effective User ID는 기본적으로는 Real UID와는 같다, 실행 파일의 setuid bit가 1인 경우 해당 파일 소유자의 UID가 effective UID가 된다.

 

Getting/Setting user IDs

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

uid_t getuid(void);
uid_t geteuid(void);

return: 얻으려고 하는 uid

uid와 euid를 반환 받는 getuid와 geteuid 함수이다.

 

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

int seteuid(uid_t uid);

Process의 EUID를 설정할 수 있는 seteuid 함수이다.

 

바로 실습으로 가보도록 하자.

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

int main(void){
	uid_t uid, euid;

	uid = getuid();
	euid = geteuid();
	printf("[Init] UId = %d, EUID = %d\n", (int)uid, (int)euid);

	seteuid(getuid());
	int cur_euid = (int)geteuid();
	printf("[seteuid(uid)] UID = %d, EUID = %d\n", (int)uid, (int)cur_euid);

	seteuid(euid);
	cur_euid = (int)geteuid();
	printf("[seteuid(euid)]UID = %d, EUID = %d\n", (int)uid, (int)cur_euid);

	return 0;
}

이렇게 프로세스 내에서의 userID 변동을 살펴 볼 수 있다.

 

User information

우선 /etc/passwd 라는 파일을 한 번 보도록 하자.

이렇게 사용자들의 정보가 쭉 나온다.

LoginID:PassWord:UID:GID:UserInfo:HomeDir의 정보로 출력이 된다.

 

당연히 시스템 콜에서도 사용자의 정보를 불러 올 수 있고, 사용자에 대한 구조체가 존재한다.

struct passwd {
	char	*pw_name;		/* user name */
	char	*pw_passwd;		/* encrypted password */
	uid_t	pw_uid;			/* user uid */
	gid_t	pw_gid;			/* user gid */
	char	*pw_gecos;		/* Honeywell login info */
	char	*pw_dir;		/* home directory */
	char	*pw_shell;		/* default shell */
};

이렇게 존재한다.

 

Reading the passwd file

#include <sys/types.h>
#include <pwd.h>

struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);

passwd 파일을 가져오는 함수들이다.

-name

passwd file에서 정보를 읽어올 사용자의 이름

-uid

passwd file에서 정보를 읽어올 사용자의 uid

return: 해당 사용자에 대한 passwd structure가 저장된 pointer, -1(NULL)

 

#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>

int main(void){
	struct passwd * pw;

	pw = getpwuid(getuid());
	printf("UID : %d\n", (int)pw->pw_uid);
	printf("Login Name : %s\n", pw->pw_name);

	return 0;
}

uid로 사용자를 불러온 모습이다.

 

Reading the group file

#include <sys/types.h>
#include <grp.h>

struct group *getgrname(const char *name);
struct group *getgrgid(gid_t gid);

struct group *getgrent(void);
void setgrent(void);
void endgrent(void);

사용자 뿐만 아니라 그룹에 대한 정보도 가져올 수 있다.

위와 비슷하고 그룹이란 것만 변경이 되었기 때문에 바로 실습으로 가보도록 하겠다.

 

group의 구조체는 아래와 같다.

struct group {
	char	*gr_name;		/* [XBD] group name */
	char	*gr_passwd;		/* [???] group password */
	gid_t	gr_gid;			/* [XBD] group id */
	char	**gr_mem;		/* [XBD] group members */
};

 

그룹에 대한 정보를 읽어오는 프로그램을 만들어보자.

 

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

int main(void){
	struct group *grp;

	grp = getgrnam("root");
	printf("Group Name : %s\n", grp->gr_name);
	printf("GID : %d\n", (int)grp->gr_gid);


	return 0;
}

그룹의 이름을 이용해 정보를 가져왔다.

  • 시간 정보

리눅스의 시간은 1970년 1월 1일 0시 0분 0초를 기준으로 흘러가며 현재까지 경과한 시간을 초 단위로 저장한다.

 

Getting time

#include <sys/time.h>

time_t time(time_t *tloc);

-tloc

얻어올 초를 저장할 주소

return: 얻어온 초, -1(error)

 

하지만 이렇게 얻어온 시간은 흘러간 초로 표현이 되기 때문에 읽기가 매우 힘들다.

그래서 이 time_t를 우리가 보기 편한 시간으로 보여주는 함수가 있고 그 시간을 저장하는 구조체가 있다.

 

우선 해당 구조체부터 살펴보자면

struct tm {
	int	tm_sec;		/* seconds after the minute [0-60] */
	int	tm_min;		/* minutes after the hour [0-59] */
	int	tm_hour;	/* hours since midnight [0-23] */
	int	tm_mday;	/* day of the month [1-31] */
	int	tm_mon;		/* months since January [0-11] */
	int	tm_year;	/* years since 1900 */
	int	tm_wday;	/* days since Sunday [0-6] */
	int	tm_yday;	/* days since January 1 [0-365] */
	int	tm_isdst;	/* Daylight Savings Time flag */
};

이렇게 연부터 초들이 들어가게 되고

 

Seconds <-> tm struct

#include <time.h>

struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);

time_t mktime(struct tm *tm);

-*timep

변환하려는 time_t 구조체

return: 변환된 tm 구조체

 

-tm

변환하려는 tm 구조체

return: 변환된 time_t 구조체

 

변환하여 출력해보자.

#include <time.h>
#include <stdio.h>

int main(void){
	struct tm *tm;
	time_t t;

	time(&t);
	printf("Time(sec) : %d\n", (int)t);

	tm = gmtime(&t);
	printf("GMTIME=Y:%d", tm->tm_year);
	printf("M:%d ", tm->tm_mon);
	printf("D:%d ", tm->tm_mday);
	printf("H:%d ", tm->tm_hour);
	printf("M:%d ", tm->tm_min);
	printf("S:%d\n", tm->tm_sec);

	tm = localtime(&t);
	printf("LOCALTIME=Y:%d", tm->tm_year);
	printf("M:%d ", tm->tm_mon);
	printf("D:%d ", tm->tm_mday);
	printf("H:%d ", tm->tm_hour);
	printf("M:%d ", tm->tm_min);
	printf("S:%d\n", tm->tm_sec);

	return 0;
}

이렇게 tm과 time_t 간에 변환이 된 모습이다.

+ Recent posts