바이너리는 디스크 같은 저장 장치에 기록되어 있는 컴파일된 실행할 수 있는 코드를 말한다. 바이너리는 흔히 프로그램을 지칭하기도 한다. 때로는 애플리케이션을 뜻하기도 한다.
프로세스는 실행중인 프로그램이다. 프로세스는 메모리에 적재된 바이너리 이미지와 가상화된 메모리의 인스턴스, 열린 파일 같은 커널 리소스, 관련된 사용자 정보와 같은 보안 정보와 하나 이상의 스레드를 포함하고 있다. 스레드는 프로세스 내 실행 단위다. 각각의 스레드는 저마다의 가상화된 프로세서를 가지고 있으며 여기에는 스택과 레지스터, 명령어 포인터 같은 프로세서의 상태가 포함되어 있다.
싱글 스레드 프로세스는 프로세스가 곧 스레드가 된다. 여기에는 가상화된 메모리 인스턴스 하나와 가상 프로세서 하나가 존재한다. 멀티스레드 프로세스에는 당연히 스레드가 여러 개 존재한다. 그 프로세스와 관련된 가상 메모리는 모든 스레드가 같은 주소 공간을 공유하게 된다.
모든 프로세스는 Process ID라는 유일한 식별자로 구분된다. pid는 특정 시점에서 유일한 값임을 보장한다.
동작 중인 다른 프로세스가 없을 때 커널이 실행하는 idle 프로세스는 pid가 0이다. 시스템의 부팅이 끝나면 커널이 실행하는 최초 프로세스인 init 프로세스의 pid는 1이다.
보통 커널의 최대 pid 값은 327268이다. 그 이유는 pid 값으로 부호형 16비트 정수를 사용했던 오래된 유닉스 시스템과의 호환성을 위한 것이다.
커널은 pid를 엄격하게 할당한다. 현재 할당된 값이 17이라면 다음에 실행되는 프로세스의 pid는 18이며 이 프로세스가 시작되는 시점에 pid가 17인 프로세스가 더 이상 실행되지 않는다고 해도 새로운 프로세스의 pid는 18이 된다. pid 값이 최대 값에 도달해서 처음부터 다시 할당하기 전까지는 앞선 pid 값이 비어 있더라도 재사용되지 않는다.
새로운 프로세스를 생성하는 프로세스를 부모 프로세스라고 하고, 새롭게 생성된 프로세스를 자식 프로세스라고 한다. init 프로세스를 제외한 모든 프로세스는 다른 프로세스로부터 생성된다. 그래서 모든 자식 프로세스에는 부모 프로세스가 있다.
모든 프로세스는 사용자와 그룹이 소유하고 있다. 여기서 말하는 소유란, 리소스에 대한 접근 권한을 제어하기 위해 사용된다. 커널 입장에서 사용자와 그룹은 단순한 정수 값이다.
프로그램에서 pid는 pid_t 자료형으로 표현되며 이는 <sys/types.h> 해더 파일에 정의되어 있다. pid_t 자료형의 실제 C 자료형은 아키텍쳐에서 정의하며 C 표준에는 정의되어 있지 않지만, 리눅스에서 pid_t는 보통 C의 int 자료형에 대한 typedef이다.
getpid() 시스템 콜은 호출한 프로세스의 pid를 반환한다.
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
getppid() 시스템 콜은 호출한 프로세스의 부모 프로세스의 pid를 반환한다.
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
getpid()와 getppid() 모두 에러를 반환하지 않으며 사용법은 간단하다.
printf("My pid=%jd\n", (intmax_t) getpid());
printf("Parent's pid=%jd\n", (intmax_t) getppid());
이 예제에서 반환값을 intmax_t 자료형으로 변환했는데 이는 C/C++ 자료형으로, 시스템에서 지원하는 어떤 singed 정수라도 저장할 수 있도록 해준다. 다시 말하면 이 자료형은 시스템의 모든 singed 정수형보다 크거나 같은 정수형 타입이다.