- 하드웨어 추상화
- 다중화(multiplex)
- 격리(isolation)
- 공유(sharing)
- 보안(security)
- 성능(performance)
- 프로세스(a running program)
- 메모리 할당
- 파일 내용
- 파일 이름, 디렉토리
- 엑세스 컨트롤(보안)
- 기타(user, IPC, network, time, terminal)
- 응용프로그램은 시스템 콜을 통해 OS의 기능을 사용
- 유저의 코드를 절대로 supervisor mode에서 실행시켜 주면 안됨
- 유저 모드에서 커널 모드로 전환
sudo apt install libcap-dev libacl1-dev libselinux1-dev libseccomp-dev libc6-dev-armhf-cross
-x86
sudo apt install libcap-dev libacl1-dev libselinux1-dev libseccomp-dev gcc-multilib
- tlpi-dist/fileio/copy.c
- 입력파일에서 바이트를 읽어서, 출력 파일에 쓰기
- open()은 파일을 생성하고 파일 디스크립터를 반환(오류시 -1)
- fd는 작은 정수
- 리눅스 커널은 프로세스마다 fd 인덱스를 테이블로 관리
- 다른 프로세스는 다른 fd 네임스페이스를 가짐
- 마치 함수 호출처럼 보임, 하지만 실제적으로는 특별한 명령어
- 유저 레지스터 저장
- supervisor mode 로 전환
- 커널 entry point 로 진입
- sys open()
- 파일 시스템에서 이름 찾고 커널 자료 구조 수정
- 유저 레지스터 복구
- 유저 모드로 전환
/* Listing 4-1 */
/* copy.c
Copy the file named argv[1] to a new file named in argv[2].
*/
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"
#ifndef BUF_SIZE /* Allow "cc -D" to override definition */
#define BUF_SIZE 1024
#endif
int
main(int argc, char *argv[])
{
int inputFd, outputFd, openFlags;
mode_t filePerms;
ssize_t numRead;
char buf[BUF_SIZE];
if (argc != 3 || strcmp(argv[1], "--help") == 0)
usageErr("%s old-file new-file\n", argv[0]);
/* Open input and output files */
inputFd = open(argv[1], O_RDONLY);
if (inputFd == -1)
errExit("opening file %s", argv[1]);
openFlags = O_CREAT | O_WRONLY | O_TRUNC;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH; /* rw-rw-rw- */
outputFd = open(argv[2], openFlags, filePerms);
if (outputFd == -1)
errExit("opening file %s", argv[2]);
/* Transfer data until we encounter end of input or an error */
while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
if (write(outputFd, buf, numRead) != numRead)
fatal("write() returned error or partial write occurred");
if (numRead == -1)
errExit("read");
if (close(inputFd) == -1)
errExit("close input");
if (close(outputFd) == -1)
errExit("close output");
exit(EXIT_SUCCESS);
}
- 새로운 프로세스를 생성하는 시스템 콜
- Shell: 커맨드로 새로운 프로세스를 생성함.
• 예: $echo hello- fork(): 시스템 콜은 새로운 프로세스를 생성
- 리눅스 커널은 호출 한 프로세스의 자원을 복사하여 생성함
• Code, Data, Registers, File descriptors, current directory
• 차이점? 반환하는 PID
• 부모: pid, 자식: 0
/* Listing 24-5 */
/* fork_whos_on_first.c
Parent repeatedly creates a child, and then processes both race to be the
first to print a message. (Each child terminates after printing its message.)
The results of running this program give us an idea of which of the two
processes--parent or child--is usually scheduled first after a fork().
Whether the child or the parent is scheduled first after fork() has
changed a number of times across different kernel versions.
*/
#include <sys/wait.h>
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
int numChildren, j;
pid_t childPid;
if (argc > 1 && strcmp(argv[1], "--help") == 0)
usageErr("%s [num-children]\n", argv[0]);
numChildren = (argc > 1) ? getInt(argv[1], GN_GT_0, "num-children") : 1;
setbuf(stdout, NULL); /* Make stdout unbuffered */
for (j = 0; j < numChildren; j++) {
switch (childPid = fork()) {
case -1:
errExit("fork");
case 0:
printf("%d child\n", j);
_exit(EXIT_SUCCESS);
default:
printf("%d parent\n", j);
wait(NULL); /* Wait for child to terminate */
break;
}
}
exit(EXIT_SUCCESS);
}
- 실행 파일로 호출한 프로세스를 변경
• 실행 파일(프로그램) -> 호출한 프로세스- $echo a b c
- 프로그램은 컴파일러에 의해 명령어와 메모리 초기값을 저장함.
- exec()은 호출한 현재 프로세스를 교체
• 호출한 프로세스의 명령어와 메모리 값은 버림
• 파일로 부터 새로운 명령어와 메모리 값을 읽음
• 열린 파일 디스크립터는 계속 유지- exec(파일 이름, 인자)
/* Listing 27-5 */
/* t_execl.c
Demonstrate the use of execl() to execute printenv(1).
*/
#include <stdlib.h>
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
printf("Initial value of USER: %s\n", getenv("USER"));
if (putenv("USER=britta") != 0)
errExit("putenv");
/* exec printenv to display the USER and SHELL environment vars */
execl("/usr/bin/printenv", "printenv", "USER", "SHELL", (char *) NULL);
errExit("execl"); /* If we get here, something went wrong */
}
- 프로세스간 통신을 위한 기법
• ls | grep –nr simple*- pipe() 시스템 콜은 두 개의 FD를 생성함
• 첫번째는 read를 위한 FD
• 두 번째는 write를 위한 FD
/* Listing 44-2 */
/* simple_pipe.c
Simple demonstration of the use of a pipe to communicate
between a parent and a child process.
Usage: simple_pipe "string"
The program creates a pipe, and then calls fork() to create a child process.
After the fork(), the parent writes the string given on the command line
to the pipe, and the child uses a loop to read data from the pipe and
print it on standard output.
*/
#include <sys/wait.h>
#include "tlpi_hdr.h"
#define BUF_SIZE 10
int
main(int argc, char *argv[])
{
int pfd[2]; /* Pipe file descriptors */
char buf[BUF_SIZE];
ssize_t numRead;
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s string\n", argv[0]);
if (pipe(pfd) == -1) /* Create the pipe */
errExit("pipe");
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Child - reads from pipe */
if (close(pfd[1]) == -1) /* Write end is unused */
errExit("close - child");
for (;;) { /* Read data from pipe, echo on stdout */
numRead = read(pfd[0], buf, BUF_SIZE);
if (numRead == -1)
errExit("read");
if (numRead == 0)
break; /* End-of-file */
if (write(STDOUT_FILENO, buf, numRead) != numRead)
fatal("child - partial/failed write");
}
write(STDOUT_FILENO, "\n", 1);
if (close(pfd[0]) == -1)
errExit("close");
exit(EXIT_SUCCESS);
default: /* Parent - writes to pipe */
if (close(pfd[0]) == -1) /* Read end is unused */
errExit("close - parent");
if (write(pfd[1], argv[1], strlen(argv[1])) != strlen(argv[1]))
fatal("parent - partial/failed write");
if (close(pfd[1]) == -1) /* Child will see EOF */
errExit("close");
wait(NULL); /* Wait for child to finish */
exit(EXIT_SUCCESS);
}
}