System Programming Ch15 - Ch16
💡 Chap_15
Pipes
(pipe는 개념자체가 그렇게 어렵지 않기 때문에 단답형으로 나올 확률 큼)
pipe
- half - duplex : 보통 반이중이라고 함, 단방향, 특정시점에는 한명만 통신가능, 한명은 sender고 한명은 reciever
(그림 외울 필요는 X, 이해만 하면 됨)
- pipe라는 함수 인자는 file descriptor 배열 2개
- 반이중이기 때문에 입력 하나, 출력 하나로 2개임
- 0은 입력, 1은 출력
위 구조는
- parent의 0이 child의 1, parent의 1이 child의 0으로 들어감
- parent와 child process 간에 서로 입력을 주고받는 것
- 이럴때는 반이중이 아님
- pipelined으로 주고받는 채팅 프로그램 만들때는 이렇게 둘 다 씀
- send와 recieve를 둘 다 열어두는 것임
이건 parent 출력, child recieve → 부모는 쓰기만 하고, child는 받기만 함
#include "apue.h"
int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0)
err_sys("pipe error");
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid > 0) { /* parent */
close(fd[0]); //입력 안받음
write(fd[1], "hello world\n", 12); //hello world를 출력할 것임
} else { /* child */
close(fd[1]); //출력 안함
n = read(fd[0], line, MAXLINE); //입력만 받음
write(STDOUT_FILENO, line, n);
}
exit(0);
}
if (pipe(fd) < 0)
에서 fd 는int fd[2];
- pipe라는 함수를 수행했을 때 인자로 fd를 넣어준다는 것은,
fd 0에는 입력, 1에는 출력을 할당하겠다는 것임
if ((pid = fork()) < 0)
: fork를 했는데 0보다 작으면 child process만드는 것을 실패했으니 에러, 정상적으로 동작했다면 parent는 0보다 큰 양수값(child process id)을 가질 것이고, 자식 프로세스 그 자신은 pid값이 0일 것임- 하나의 코드 안에서 두개의 프로세스를 제어하다 보니까 if 문과 else문으로 나뉘게 되는 것
cat file | pager
pager : 책을 넘기는 것처럼 모니터 안에 표현할 수 없을 만큼 많은 양이 들어오면 다음장으로 넘겨주는 기능
- cat file로 파일을 읽었는데, 한 모니터에 출력하지 못할만큼 너무 길면 pager로 다음장으로 넘기는 기능을 추가해주라는 것
- 구현하려면 pipe fork dup exec 함수를 써서 구현해야됨
- 그걸 구현한 코드가 밑에 코드
시험에 무조건 나옴!! 코드 외워야됨
#include "apue.h"
#include <sys/wait.h>
#define DEF_PAGER "/bin/more"
int main(int argc, char *argv[])
{
int n;
int fd[2];
pid_t pid;
char *pager, *argv0;
char line[MAXLINE];
FILE *fp;
if (argc != 2)
err_quit("usage: a.out <pathname>");
//인자가 하나일 경우에만 동작
if ((fp = fopen(argv[1], "r")) == NULL) //첫번째 인자로 들어온 애를 읽기 권한으로 열음
err_sys("can't open %s", argv[1]);
if (pipe(fd) < 0) //pipe 만들었음
err_sys("pipe error");
if ((pid = fork()) < 0) { //fork 0보다 작으면 에러
err_sys("fork error");
} else if (pid > 0) { //0보다 크니까 parent
**close(fd[0]);** //parent는 입력을 안함, 출력만(쓰기만)
/* parent copies argv[1] to pipe */
**while (fgets(line, MAXLINE, fp)!= NULL)** { //fp로부터 문자열을 입력받겠다는 것, MAXLINE만큼
n = strlen(line);
**if (write(fd[1], line, n) != n)** //fd[1]로 write를 함, line,즉 파일의 끝까지
err_sys("write error to pipe");
} //파일을 다 읽을 때까지 while문 돌고
if (ferror(fp))
err_sys("fgets error");
/* close write end of pipe for reader */
**close(fd[1]);** //출력하는 pipe 닫음
**if (waitpid(pid, NULL, 0) < 0)** //자식프로세스가 끝날때까지 기다림
err_sys("waitpid error");
exit(0);
} else { //pid가 0인 경우, 자식 프로세스
**close(fd[1]);** //자식 프로세스는 읽기만 함, 출력 x
**if (fd[0] != STDIN_FILENO) {** //입력이 아니면
**if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)** //dup2, 파일디스크립터를 복사, fd[0]를 입력받는 파일 디스크립터로 강제로 복사해줘라
err_sys("dup2 error to stdin"); //그래도 입력받는애로 세팅이 안되면 에러
/* don't need this after dup2 */
close(fd[0]);
}
/* get arguments for execl() */
if ((pager = getenv("PAGER")) == NULL)
pager = DEF_PAGER;
if ((argv0 = strrchr(pager, '/')) != NULL)
argv0++;
else
argv0 = pager;
**if (execl(pager, argv0, (char *)0) < 0)** //execl로 pager 명령어 수행
err_sys("execl error for %s", pager);
}
exit(0);
}
💡 Chap_16
Socket Types
tcp와 udp의 차이가 중요!
- tcp : STREAM, 연결지향성, 신뢰성, 안정적
- udp : DATAGRAM, 비연결성, 비신뢰성, 막무가내
Byte Ordering
시스템프로그래밍, 시스템해킹의 꽃
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostint32);
uint16_t htons(uint16_t hostint16);
uint32_t ntohl(uint32_t netint32);
uint16_t ntohs(uint16_t netint16);
- hton : host to network (l은 long, s는 short)
- 내가 지금 pc에서 쓰고 있는 little-endian 방식을 network에서 쓰고 있는 big-endian 방식으로 바꾼다는 것
- ntoh : network to host
- network에서 사용하고 있는 big-endian 방식을 host에서 사용하는 litte-endian 방식으로 바꿈
- Big-endian : 순서대로, 대소비교가 편해짐, 네트워크에서 사용(ip로 pc를 구분하기 때문에)
- Little-endian : 거꾸로 저장, 홀짝구분이 편해짐, 컴퓨터시스템에서 사용
Socket System Calls for Connection-Oriented Protocol
연결지향성 TCP, 신뢰성 있음
- 왼쪽서버에서 소켓 함수를 부름 → 휴대폰 하나 사는 것
- bind 함수 → 유심 꽂고 전화번호 부여 받는 것
- listen → 전화 올때까지 대기
- accept → 전화온거 통화버튼 슬라이드
- client → 소켓, 전화를 하나 삼, 근데 전화를 받진 못하고 할 수만 있는것, 대포폰 → bind를 하지 않음, 소켓하나로 바로 서버에 연결(connect)
Socket System Calls for Connectionless Protocol
비연결성 UDP, 신뢰성 없음
- 서버쪽에서 서버를 만들고(socket), 유심 꽂고(bind), 데이터 올때까지 기다려(recvfrom)
- listen과 accept가 없음
- client쪽에서는 socket하고 bind 받아야됨
TCP vs UDP
- TCP는 client의 id를 bind하지 않아도 서버에 의하여 식별할 수 있음 (handler가 생겨서)
- UDP는 client에서 id를 서버로 알려줘야함 (연결성이 없기 때문에)
Date Transfer
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen);
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen);
- TCP에서 쓰는 함수 : send, recv
- UDP에서 쓰는 함수 : sendto, recvfrom