시스템 프로그래밍 11-1(Signals and Nonlocal Jumps)

TonyHan·2021년 5월 27일
0
post-thumbnail

Limitation of Ordinary Pipe

¢ Ordinary pipes only allow a pair of processes to communicate.
¢ Ordinary pipes exit only while the processes are
communicating with one another.
§ Once the processes have finished communicating and have terminated, the ordinary pipe ceases to exit.
¢ Named Pipe?
§ Provide a much more powerful communication tool

Named Pipe (FIFO)

¢ Communication is bi-directional, and no parent-child
relationship.
¢ Entries related pipes are managed in the file system.
¢ A named pipe can have several writers.
¢ Named pipes continue to exist after communicating processes have finished.
¢ Has a name and handled exactly like files with respect to file operations (e.g., open, close, read, write).
¢ Created by mkfifo or mknod commands.
¢ Can be created by C functions : mkfifo().
§ int mkfifo(const char * path, mode_t mode);
¢ Reading from / writing to a named pipe can be achieved by using
standard read() and write() system calls.

Named Pipe (Producer and Consumer)

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() 
{ 
int fd; char * myfifo = "/tmp/myfifo"; 
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666); 
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY); 
write(fd, "Hi", sizeof("Hi")); 
close(fd); 
/* remove the FIFO */
unlink(myfifo); 
return 0; 
}

writer.c reader.c

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main() 
{ 
int fd; char * myfifo = "/tmp/myfifo"; 
char buf[MAX_BUF]; 
/* open, read, and display the message 
from the FIFO */
fd = open(myfifo, O_RDONLY); 
read(fd, buf, MAX_BUF); 
printf("Received: %s\n", buf); 
close(fd); 
return 0; 
}

Open Write-Only with and without O_NONBLOCK

¢ Write-Only without O_NONBLOCK
§ open blocks and wait until another process opens the FIFO for reading.

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() 
{ 
int fd; char * myfifo = "/tmp/myfifo"; 
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666); 
/* write "Hi" to the FIFO */
printf("open without O_NONBLOCK\n");
fd = open(myfifo, O_WRONLY); 
write(fd, "Hi", sizeof("Hi")); 
close(fd); 
/* remove the FIFO */
unlink(myfifo); 
return 0; 
}

Output
Waiting for another process to open
the FIFO

3. Problem with Simple Shell Example

앞의 simple shell의 문제점은 이 shell이 foreground task가 끝날때까지 기다리고 끝난다음에 reaping을 할 수 있다.

  • Our example shell correctly waits for and reaps foreground jobs

하지만 &과 같은 background process는 어떻게 처리할 것인가가 문제가 된다.

  • But what about background jobs?
    • Will become zombies when they terminate
    • Will never be reaped because shell (typically) will not terminate
    • Will create a memory leak that could run the kernel out of memory

만약 reaping 안해주면 좀비가 된다.
쉘이 절때 끝나지 않으면 parent process의 프로세스가 안끝나니 자식 reaping도 안된다.

결국에는 memory leak가 발생하게 된다. 즉 메모리 누스가 발생할 수 있다.

ECF to the Rescue!

그래서 위의 문제를 해결하기 위한 것이 ECF이다.(Exception Control flow) 어떤 프로세스한테 alert해주는 매커니즘이다. bg 프로세스가 끝났을때 kernel이 특정 프로세스에게 끝났다는 것을 알려준다. 그것이 signal이다.

  • Solution: Exceptional control flow
    • The kernel will interrupt regular processing to alert us when a background process completes
    • In Unix, the alert mechanism is called a signal

=== Signals ===

1. Signals

signal은 어떤 프로세스에 어떤 이벤트가 발생했다고 알려주는 메세지라고 생각하면 된다. excpeotion, interrupt와 유사

kernel이 process에게 보내준다.
signal이 도착했을떄 그 안에는 ID, fact가 들어가 있다.

  • A signal is a small message that notifies a process that an event of some type has occurred in the system
    • Akin to exceptions and interrupts
    • Sent from the kernel (sometimes at the request of another process) to a process
    • Signal type is identified by small integer ID’s (1-30)
    • Only information in a signal is its ID and the fact that it arrived

SIGKILL : 어떤 프로세스를 죽여달라는 것
SIGSEGV : 프로세스가 어떤 메모리를 접근할때 메모리에 올라가 있지 않은 곳을 접근할때 OS가 kernel을 통해 SIGSEGV를 보내게 되고 프로세스가 이걸 받으면 종료를 한다.
SIGALRM : 하드웨어 타이머가 process마다 10ms가 지나면 하드웨어 interrupt가 발생하고 프로세스에다가 signal을 보내게 된다. signal을 받으면 프로세스가 종료된다.
SICHILD : child가 종료시 parent한테 보내는 메세지이다.

Signal Concepts: Sending a Signal

  • Kernel sends (delivers) a signal to a destination process by updating some state in the context of the destination process

커널이 Pdest에 signal을 보낸다는 행위자체는 프로세스가 사용하고 있는 context 중에 있는 어떤 state를 바꾼다

  • Kernel sends a signal for one of the following reasons:
    • Kernel has detected a system event such as divide-by-zero (SIGFPE) or the termination of a child process (SIGCHLD)
    • Another process has invoked the kill system call to explicitly request the kernel to send a signal to the destination process

다음과 같은 이유가 있을 수 있다.
1. 0으로 나눌려고 하는 경우, child종료가 확인된 경우 parent에게 메세지를 보낼때
2. kill command를 사용한 경우

Signal Concepts: Receiving a Signal

signal이 delivered 되었다와 received되었다는 이야기도 한다.

delivered되었다는 것은 process에다가 메세지를 보낸것이다. 하지만 프로세스가 아직 그 메세지에 대해 확인은 하지 않았다. 그리고 그 메세지를 process가 처리하면 receive 했다고 한다.

  • A destination process receives a signal when it is forced by the kernel to react in some way to the delivery of the signal

메세지를 받으면 다음과 같은 행동을 할 수 있다.

  • Some possible ways to react:
    • Ignore the signal (do nothing)
    • Terminate the process (with optional core dump)
    • Catch the signal by executing a user-level function called signal handler
      • Akin to a hardware exception handler being called in response to an asynchronous interrupt:

Signal Concepts: Pending and Blocked Signals

signal이 전달되었으면 pending되었다고 한다. 아직 receive안했다. 특정 signal은 하나만 있을 수 있다. 즉 동일한 시그널을 받아서 queueing해줄 수 없다.

만약 동일한 signal이 오면 discard한다.

  • A signal is pending if sent but not yet received
    • There can be at most one pending signal of any particular type
    • Important: Signals are not queued
      • If a process has a pending signal of type k, then subsequent signals of type k that are sent to that process are discarded

프로세스가 특정 signal을 blocking할 수 있다.

  • A process can block the receipt of certain signals
    • Blocked signals can be delivered, but will not be received until the signal is unblocked
  • A pending signal is received at most once

Signal Concepts: Pending/Blocked Bits

Pending bit vector

Blocked bit vector

  • Kernel maintains pending and blocked bit vectors in the context of each process
    • pending: represents the set of pending signals
      • Kernel sets bit k in pending when a signal of type k is delivered
      • Kernel clears bit k in pending when a signal of type k is received
    • blocked: represents the set of blocked signals
      • Can be set and cleared by using the sigprocmask function
      • Also referred to as the signal mask.

2. Sending Signals: Process Groups

각 Process들은 Process Group을 가지고 있다.

  • Every process belongs to exactly one process group

프로세스 별로 pgid가 있다.

Sending Signals with /bin/kill Program

  • /bin/kill program sends arbitrary signal to a process or process group

  • Examples

    • /bin/kill –9 24818
      Send SIGKILL to process 24818
      현재 24818, 24819의 프로세스 두개가 돌고 있다고 하자. 이때 kill command는 24818이라는 프로세스에게 signal을 보내라고 한다.
    • /bin/kill –9 –24817
      Send SIGKILL to every process in process group 24817
      만약에 내가 process 그룹안에 있는 모든 프로세스를 죽이고자 한다면 -를 붙이면 된다.

Sending Signals from the Keyboard

  • Typing ctrl-c (ctrl-z) causes the kernel to send a SIGINT (SIGTSTP) to every job in the foreground process group.
    • SIGINT – default action is to terminate each process : process가 terminate된다.
    • SIGTSTP – default action is to stop (suspend) each process : process를 suspend하게 만들 수 있다.

Example of ctrl-c and ctrl-z

Sending Signals with kill Function

void fork12()
{
	pid_t pid[N];
	int i;
	int child_status;

N = 5라고 하자 그래서 5개의 프로세스가 돌고 있는 상태이다.
	for (i = 0; i < N; i++)
		if ((pid[i] = fork()) == 0) {
		/* Child: Infinite Loop * /
			while(1)
			;
		}
kill command들 가지고 SIGINT를 보내게 된다. 그러면 각각 가지고 있는 pending bit vector의 해당하는 부분을 1로 셋팅해준다.
	for (i = 0; i < N; i++) {
		printf("Killing process %d\n", pid[i]);
		kill(pid[i], SIGINT);
	}
	for (i = 0; i < N; i++) {
		pid_t wpid = wait(&child_status);
		if (WIFEXITED(child_status))
			printf("Child %d terminated with exit status %d\n",
					wpid, WEXITSTATUS(child_status));
		else
			printf("Child %d terminated abnormally\n", wpid);
	}
} forks.c

3. Receiving Signals

  • Suppose kernel is returning from an exception handler and is ready to pass control to process p

timer interrupt가 발생하면 context switch를 하게 되고 processB를 실행하기 바로 이전에 processB가 가지고 있는 bit vector을 확인하게 되고 signal이 있는지 없는지 확인해서 user code를 실행하게 된다.

  • Suppose kernel is returning from an exception handler and is ready to pass control to process p

  • Kernel computes pnb = pending & ~blocked

    • The set of pending nonblocked signals for process p
      signal을 체크할떄 pendingrhk ~blocked bit를 체크해준다.
  • If (pnb == 0)

    • Pass control to next instruction in the logical flow for p
  • Else

    • Choose least nonzero bit k in pnb and force process p to receive signal k
    • The receipt of the signal triggers some action by p
    • Repeat for all nonzero k in pnb
    • Pass control to next instruction in logical flow for p
      0 이 아니라면 bit vector을 돌아가면서 0이 아닌 k를 실행하게 된다.

Default Actions

  • Each signal type has a predefined default action, which is
    one of:
    • The process terminates
    • The process stops until restarted by a SIGCONT signal
    • The process ignores the signal

4. Installing Signal Handlers

Handler를 등록할 수 있는데 등록하면 installing이라고 하고 실행하면 catching or handling이라고 한다.

  • The signal function modifies the default action associated with the receipt of signal signum
    • handler_t * signal(int signum, handler_t * handler)
  • Different values for handler:
    • SIG_IGN: ignore signals of type signum
    • SIG_DFL: revert to the default action on receipt of signals of type signum
    • Otherwise, handler is the address of a user-level signal handler
    • Called when process receives signal of type signum
    • Referred to as “installing” the handler
    • Executing handler is called “catching” or “handling” the signal
    • When the handler executes its return statement, control passes back to instruction in the control flow of the process that was interrupted by receipt of the signal

Signal Handling Example


위와 같이 등록하게 되면 sigint_handler를 수행하게 된다.

SIGINT를 보내면 sigint_handler를 수행하게 된다.

5. Signals Handlers as Concurrent Flows

  • A signal handler is a separate logical flow (not process) that runs concurrently with the main program


concurrent하게 실행되는게 아니라 돌아오는 과정 중간에 문제가 생기면 handler를 수행한다음에 ProcessA가 실행되게 된다.

Another View of Signal Handlers as Concurrent Flows

A 실행하다가 signal을 받아서 context switch가 되어 다른 코드를 수행하고 다시 돌아오는 시점에 프로세스가 signal을 받을 수 있다.

그래서 이만큼의 시간동안에는 signal이 왔다는 것을 알 수 없다.

profile
신촌거지출신개발자(시리즈 부분에 목차가 나옵니다.)

0개의 댓글