¢ 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
¢ 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.
#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;
}
¢ 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
앞의 simple shell의 문제점은 이 shell이 foreground task가 끝날때까지 기다리고 끝난다음에 reaping을 할 수 있다.
하지만 &
과 같은 background process는 어떻게 처리할 것인가가 문제가 된다.
만약 reaping 안해주면 좀비가 된다.
쉘이 절때 끝나지 않으면 parent process의 프로세스가 안끝나니 자식 reaping도 안된다.
결국에는 memory leak가 발생하게 된다. 즉 메모리 누스가 발생할 수 있다.
그래서 위의 문제를 해결하기 위한 것이 ECF이다.(Exception Control flow) 어떤 프로세스한테 alert해주는 매커니즘이다. bg 프로세스가 끝났을때 kernel이 특정 프로세스에게 끝났다는 것을 알려준다. 그것이 signal이다.
signal은 어떤 프로세스에 어떤 이벤트가 발생했다고 알려주는 메세지라고 생각하면 된다. excpeotion, interrupt와 유사
kernel이 process에게 보내준다.
signal이 도착했을떄 그 안에는 ID, fact가 들어가 있다.
SIGKILL : 어떤 프로세스를 죽여달라는 것
SIGSEGV : 프로세스가 어떤 메모리를 접근할때 메모리에 올라가 있지 않은 곳을 접근할때 OS가 kernel을 통해 SIGSEGV를 보내게 되고 프로세스가 이걸 받으면 종료를 한다.
SIGALRM : 하드웨어 타이머가 process마다 10ms가 지나면 하드웨어 interrupt가 발생하고 프로세스에다가 signal을 보내게 된다. signal을 받으면 프로세스가 종료된다.
SICHILD : child가 종료시 parent한테 보내는 메세지이다.
커널이 Pdest에 signal을 보낸다는 행위자체는 프로세스가 사용하고 있는 context 중에 있는 어떤 state를 바꾼다
다음과 같은 이유가 있을 수 있다.
1. 0으로 나눌려고 하는 경우, child종료가 확인된 경우 parent에게 메세지를 보낼때
2. kill command를 사용한 경우
signal이 delivered 되었다와 received되었다는 이야기도 한다.
delivered되었다는 것은 process에다가 메세지를 보낸것이다. 하지만 프로세스가 아직 그 메세지에 대해 확인은 하지 않았다. 그리고 그 메세지를 process가 처리하면 receive 했다고 한다.
메세지를 받으면 다음과 같은 행동을 할 수 있다.
signal이 전달되었으면 pending되었다고 한다. 아직 receive안했다. 특정 signal은 하나만 있을 수 있다. 즉 동일한 시그널을 받아서 queueing해줄 수 없다.
만약 동일한 signal이 오면 discard한다.
프로세스가 특정 signal을 blocking할 수 있다.
Pending bit vector
Blocked bit vector
각 Process들은 Process Group을 가지고 있다.
프로세스 별로 pgid가 있다.
/bin/kill program sends arbitrary signal to a process or process group
Examples
-
를 붙이면 된다. 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
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
If (pnb == 0)
Else
Handler를 등록할 수 있는데 등록하면 installing이라고 하고 실행하면 catching or handling이라고 한다.
handler_t * signal(int signum, handler_t * handler)
위와 같이 등록하게 되면 sigint_handler를 수행하게 된다.
SIGINT를 보내면 sigint_handler를 수행하게 된다.
concurrent하게 실행되는게 아니라 돌아오는 과정 중간에 문제가 생기면 handler를 수행한다음에 ProcessA가 실행되게 된다.
A 실행하다가 signal을 받아서 context switch가 되어 다른 코드를 수행하고 다시 돌아오는 시점에 프로세스가 signal을 받을 수 있다.
그래서 이만큼의 시간동안에는 signal이 왔다는 것을 알 수 없다.