0 : Divide Error
13 : General protection fault : try write to read-only text segment
14 : Page fault
18 : Machine check- Hardware Error, do not give back control to process
32-255 : OS-defined exceptions
1. in runtime, process detect exception event, decide exception number k
2. make indirect procedure call by exception table k (using exception table base register+k)
1) interrupt
- asynchronous
-after current instruction, processer find interrupt pin goes high then read exception number at system bus.
call prompt interrupt handler. after handler return, give back control to next instruction
2) trap and system call
- when user program read file, fork, execuve, and quit use instruction "syscall"
- similar with functiaon call, but "syscall" work in kernal mode (normally, function work in user mode)
- use kernal stack
3)fault
- if fault causes, give control to fault handler. if handler can handle fault, give back to control to instruction that caused fault to re-execute
exampe) page fault :
when instruction refer virtual memory table but real memory page does not exists, so page fault handler load proper page. then give back to contrl to instruction.
when instruction re-execute, page is in memory, so does not cause fault
4)abort
-Fatal Hardware Error (DRAM.SRAM)
-dont giver back
system-level program
int main(){
write(1,"hello world\n",13);
_exit(0);
}
for process abstraction, processer restricted reference space and instruction from program
-> control register : mod bit
if mod bit activate, processer operate kernel mode.(process cannot activate mod bit)
exception handler(interrupt,fault,trap syscall) can operate kernel mode
linux : /proc file system-> user mode can access content of data structure of kernal mode
- /proc/cpuinfo
- /proc/process-id/maps (memory segment)
Kernal retains context by each process
Context
-General Purpose Register
-Floating Point Register
-Kernal Stack
-User stack
-Program Counter
-State
-Page Table
-Process table
-File Table
-etc
1. save context of current process
2. recover context of sleeping process
3. give control to recovered process
for example, syscall "read" ask for disk access, context switch to other process while waiting data from disk
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
// using namespace std;
int main(){
pid_t a=getpid();
pid_t b=getppid();
printf("%d ,%d",a,b);
}
25949 ,25242
-Running : Process is running or wait for running, scheduled by kernel
-Stopped : If Process get STOP, SIGTSTP, SIGTTIN, SIGTTOUG signal, process stopped and not be scheduled until get Signal "SIGCONT". (Signal : kind of Software Interrupt)
-Terminated : (got terminating signal, return from main routine, call "exit()")
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
int x=1;
pid_t ppid=getppid();
pid_t forkpid=fork();
pid_t curpid=getpid();
if(forkpid==0){
printf("child : pid-%d,ppid-%d ,fork-%d\n",curpid,ppid,forkpid);
exit(0);
}else{
printf("parent pid-%d,ppid-%d ,fork-%d\n",curpid,ppid,forkpid);
exit(0);
}
}
pid_t getpid(): return current process id
pid_t getppid(): return parents pid
pid_t fork() : return 0 at child process, return "child pid" at parent process
parent pid-32468,ppid-25242 ,fork-32469
child : pid-32469,ppid-25242 ,fork-0
pid_t waitpid(pid_t pid,int *statusp,int options) :
return child process id that make return.
int options;
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
int main(){
int x=1;
pid_t ppid=getppid();
pid_t forkpid=fork();
pid_t curpid=getpid();
if(forkpid==0){
printf("child : pid-%d,ppid-%d ,fork-%d\n",curpid,ppid,forkpid);
exit(0);
}else{
pid_t wp;
//WUNTRACED : wait before child process terminated
waitpid(-1,NULL,WUNTRACED);
//WNOHANG: if there is no termiated process, return 0
waitpid(-1,NULL,WNOHANG);
while(1){
wp=waitpid(-1,NULL,WNOHANG);
if(wp!=0){
break;
}
pid_t pid=getpid();
printf("wating...%d\n",pid);
}
// while( !(wp=waitpid(-1,NULL,WNOHANG))){
// // pid_t wp=waitpid(-1,NULL,WNOHANG);
// printf("waiting...\n");
// }
printf("wp: %d\n",wp);
printf("parent pid-%d,ppid-%d ,fork-%d\n",curpid,ppid,forkpid);
exit(0);
}
}
-WNOHANG
if any child processes do not terminated, return -1
else return child pid that makes return.
wating...40809
wating...40809
wating...40809
wating...40809
wating...40809
wating...40809
child : pid-40810,ppid-25242 ,fork-0
wating...40809
wating...40809
wating...40809
wp: 40810
parent pid-40809,ppid-25242 ,fork-40810
-WUNTRACED
wait process that call waitpid
child : pid-41087,ppid-25242 ,fork-0
wp: -1
parent pid-41086,ppid-25242 ,fork-41087
in runtime, WUNTRACED options make parents wait, so while loop breaked immediatly(wp==-1), not printf("wating...")
-WCONTINUED
wait for SIGCOUNT. SIGCONT also resume process which is stopped by SIGKILL and SIGSTOP
waitPid3.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#define N 2
#include <errno.h>
int main(){
int status,i;
pid_t pid;
for(i=0;i<N;i++){
if((pid=fork())==0) exit(100+i);
}
while((pid=waitpid(-1,&status,0))>0){
if(WIFEXITED(status)){
printf("child %d terminated normally with exit status=%d\n",pid,WEXITSTATUS(status));
}
else printf("child %d terminated abnormally\n",pid);
}
if(errno!=ECHILD){
fprintf(stderr,"%s: %d\n","waitpid error",errno);
}
}
child 50317 terminated normally with exit status=100
child 50318 terminated normally with exit status=101
child process terminated by randomly,nondeterministcally. system by system
#include <unistd.h>
#include <stdio.h>
signed int wakeup(unsigned int secs){
unsigned int rc=sleep(secs);
printf("Woke up at %d secs",rc);
return rc;
}
int main(){
wakeup(5);
}
int pause(void) : until process get SIGNAL, sleep the process.
#include <stdlib.h>
#include <stdio.h>
int main(int argc,char* argv[],char*envp[] ){
int i=0;
printf("Command-line arguments:\n");
while(argv[i]!=NULL){
printf("argv[%d]: %s\n",i,argv[i]);
i++;
}
i=0;
while(envp[i]!=NULL){
printf("envp[%d]: %s\n",i,envp[i]);
i++;
}
printf("argc : %d\n",argc);
return 0;
}
./main arg1 arg2 arg3
Command-line arguments:
argv[0]: ./main
argv[1]: arg1
argv[2]: arg2
argv[3]: arg3
envp[0]: SHELL=/bin/bash
envp[1]: SESSION_MANAGER=local/gs201-14Z90N-VR5DK:@/tmp/.ICE-unix/2097,unix/gs201-14Z90N-VR5DK:/tmp/.ICE-unix/2097
envp[2]: QT_ACCESSIBILITY=1
.
.
.
.
envp[70]: OLDPWD=/home/gs201/Desktop/csapp
envp[71]: TERM_PROGRAM=vscode
envp[72]: _=./main
argc : 4
int execve(const char*filename, const char* argv[],const char* envp[]);
-only return once(differ from fork() )
execve make another int main(int argc, char* argv[],char *envp[]);
How can shell Works--
shellex.c
#define MAXARGS 128
#include "csapp.h"
void eval(char * cmdline);
int parseline(char* buf,char**argv);
int builtin_command(char** argv);
int main(){
char cmdline[MAXLINE];
while(1){
printf("> ");
fgets(cmdline,MAXLINE,stdin);
if(feof(stdin)) exit(0);
eval(cmdline);
}
}
void eval(char *cmdline){
char* argv[MAXARGS];
char buf[MAXLINE];
int bg;
pid_t pid;
strcpy(buf,cmdline);
bg=parseline(buf,argv);
if(argv[0]==NULL) return;
if(!builtin_command(argv)){
if((pid=fork())==0){
if(execve(argv[0],argv,environ)<0){
printf("%s: Command is not Found.\n",argv[0]);
exit(0);
}
}
if(!bg){
int status;
if(waitpid(pid,&status,0)<0) fprintf(stderr,"waitfg: waitpid error");
}else{
printf("%d %s",pid,cmdline);
}
return;
}
}
int builtin_command(char ** argv){
if(!strcmp(argv[0],"quit")) exit(0);
if(!strcmp(argv[0],"&")) return 1;
return 0;
}
int parseline(char* buf,char** argv){
char* delim;
int argc;
int bg;
buf[strlen(buf)-1]=' ';
while(*buf&&(*buf)==' ') buf++;
argc=0;
while((delim=strchr(buf,' '))){
argv[argc++]=buf;
*delim='\0';
buf=delim+1;
while(*buf&&(*buf==' ')){
buf++;
}
}
argv[argc]=NULL;
if(argc==0) return 1;
if((bg=(*argv[argc-1]=='&')!=0)) argv[--argc]=NULL;
return bg;
}
example)
8 : SIGFPE : if process try divide by 0, kernal send SIGFPE
4 : SIGILL : if process execute wrong instruction, kernal send SIGILL
11 : SIGSEGV : if process acess wrong memory, kernal send SIGSEGV
2 : SIGINT : if process run on foreground, CLT+C makes kernal send SIGINT to process group
9 : SIGKILL : some process can send SIGKILL to other process to terminate.
17 : SIGCHILD : if child process stopped or terminated, kernal send SIGCHILD to parent process
pendding signal : sent, ,but not recieved
-if signal have K-type signal in one time, additional K-type signal can be aborted.(k-type signal queue can recieve only one signal)
- process belong to only one process group, process group has ID
#include <unistd.h>
#include <stdio.h>
int main(){
pid_t pid1=getpid();
pid_t pidg1=getpgrp();
printf("cur pid : %d pgroup: %d\n",pid1,pidg1);
int a;
setpgid(0,0);
printf("cur pid : %d pgroup: %d\n",pid1,pidg1);
while(1){
scanf("%d",&a);
if(a==1) break;
}
return 0;
}
/bin/kill -9 60671
9 : SIGKILL
cur pid : 60671 pgroup: 60671
cur pid : 60671 pgroup: 60671
Killed
unsigned int alarm(unsigned int secs);
- kernal send SIGALRM to process every `secs` second.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(){
pid_t pid;
if((pid=fork())==0){
// at child, fork return 0
pause(); //child pause
printf("control should never reach here\n");
}
// at parent, fork return child process
kill(pid,SIGKILL); //kill child
exit(0); //parent exit
}
-Implicit Blocking : Kernal Block sort of signal which is handling by signal handler.
-Explicit Blocking
#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
int sigemtpyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set,int signum);
int sigismember(const sigset_t *set,int signum);
Signal Handler
-operating concurrently with main program, share same global scope
-it is not intuitive to know how and when signal is recieved
-other system(not linux) have different signal handling system
making signal handler
- write handler concice and precise
- save errno and restore
- protect global data structure to block all signal
- declare global variable voliatie
volitie int g;
every access to g, read g from main memory
- declare flag sig_atomic_t
signal1.c
#include "csapp.h"
void handler1(int sig){
int olderrrno=errno;
if((waitpid(-1,NULL,0))<0) sio_error("waitpid error");
sio_puts("Handler reaped child\n");
sleep(1);
errno=olderrrno;
}
void handler2(int sig){
int olderrno=errno;
while(waitpid(-1,NULL,0)>0){
sio_puts("handler reaped child\n");
}
if(errno!=ECHILD) sio_error("wait pid error");
Sleep(1);
errno=olderrno;
}
int main(){
int i,n;
char buf[MAXBUF];
if(signal(SIGCHLD,handler2)==SIG_ERR){
unix_error("signal error");
}
for(i=0;i<3;i++){
if(Fork()==0){
printf("Hello from child %d\n",(int)getpid());
exit(0);
}
}
if( (n=read(STDIN_FILENO,buf,sizeof(buf)))<0 ){
unix_error("read");
}
printf("parent processing input\n");
while(1);
exit(0);
}
handler 1 :
SIGCHLD 1- reap
SIGCHLD 2-in the queue
SIGCHLD 3- signal handler sleeping, aborted.
Hello from child 53018
Hello from child 53019
Hello from child 53020
Handler reaped child
Handler reaped child
cr
parent processing input
handler 2 :
in the while loop ,reap all signal
->when handler is called, handler reap child porcesses as many as possible
Hello from child 54744
Hello from child 54745
Hello from child 54746
handler reaped child
handler reaped child
handler reaped child
#include "csapp.h"
void handler(int sig){
int olderrno=errno;
sigset_t mask_all,prev_all;
pid_t pid;
Sigfillset(&mask_all);
while((pid=waitpid(-1,NULL,0))>0){
Sigprocmask(SIG_BLOCK,&mask_all,&prev_all);
deletejob(pid);
Sigprocmask(SIG_SETMASK,&prev_all,NULL);
}
if(errno!=ECHILD) sio_error("wait pid error");
errno=olderrno;
}
int main(int argc,char**argv){
int pid;
sigset_t mask_all,prev_all;
Sigfillset(&mask_all);
Signal(SIGCHLD,handler);
initjobs();
while(1){
if((pid=Fork())==0){
Execve("/bin/date",argv,NULL);
}
Sigprocmask(SIG_BLOCK,&mask_all,NULL);
addjob(pid);
Sigprocmask(SIG_SETMASK,&prev_all,NULL);
}
exit(0);
}
In While loop, before Sigprocmaks(SIG_BLOCK,&mask_all,NULL) if child process exit deletejob() id called by handler.
then Block mask_all, add job.
this is bug because deletejob is call early than addjob. bug called race
#include "csapp.h"
void handler(int sig){
int olderrno=errno;
sigset_t mask_all,prev_all;
pid_t pid;
Sigfillset(&mask_all);
while((pid=waitpid(-1,NULL,0))>0){
Sigprocmask(SIG_BLOCK,&mask_all,&prev_all);
deletejob(pid);
Sigprocmask(SIG_SETMASK,&prev_all,NULL);
}
if(errno!=ECHILD) sio_error("wait pid error");
errno=olderrno;
}
int main(int argc,char**argv){
int pid;
sigset_t mask_all,mask_one,prev_one;
Sigfillset(&mask_all);
Sigemptyset(&mask_one);
Sigaddset(&mask_one,SIGCHLD);
Signal(SIGCHLD,handler);
initjobs();
while(1){
Sigprocmask(SIG_BLOCK,&mask_one,&prev_one);
if((pid=Fork())==0){
Sigprocmask(SIG_SETMASK,&prev_one,NULL); //CHILD PROCESS, child process inherit parent's BLOCKED SET
Execve("/bin/date",argv,NULL);
}
Sigprocmask(SIG_BLOCK,&mask_all,NULL);
addjob(pid);
Sigprocmask(SIG_SETMASK,&prev_one,NULL);
}
exit(0);
}
before fork, block SIGCHLD. after addjob, unblock the SIGCHLD. if SIGCHLD is in pending, handler works.
then deletejob() would be call.
#include "csapp.h"
volatile sig_atomic_t pid;
void sigchld_handler(int s){
int olderrno=errno;
pid=waitpid(-1,NULL,0);
errno=olderrno;
}
void sigint_handler(int s){
}
int main(int argc,char** argv){
sigset_t mask,prev;
Signal(SIGCHLD,sigchld_handler);
Signal(SIGINT,sigint_handler);
Sigemptyset(&mask);
Sigaddset(&mask,SIGCHLD);
while(1){
Sigprocmask(SIG_BLOCK,&mask,&prev); //block sigchld
if(Fork()==0) exit(0);
pid=0;
Sigprocmask(SIG_SETMASK,&prev,NULL); //unblock sigchld
while(!pid){
pause();
}
printf(".");
}
exit(0);
}
above code, if SIGCHLD recieve between while test and pause(), parent process would be sleep all day
sigsuspend operate next job atomically
sigprocmask(SIG_SETMASK,&mask,&prev)
pause();
sigprocmask(SIG_SETMASK,&prev,NULL);
during sleeping ,if SIGNAL recieved(sighandler working), wake up and restore state of mask before sigsuspend execute.
#include "csapp.h"
volatile sig_atomic_t pid;
void sigchld_handler(int s){
int olderrno=errno;
pid=waitpid(-1,NULL,0);
errno=olderrno;
}
void sigint_handler(int s){
}
int main(int argc,char** argv){
sigset_t mask,prev;
Signal(SIGCHLD,sigchld_handler);
Signal(SIGINT,sigint_handler);
Sigemptyset(&mask);
Sigaddset(&mask,SIGCHLD);
while(1){
Sigprocmask(SIG_BLOCK,&mask,&prev);
if(Fork()==0) exit(0);
pid=0;
while(!pid){
sigsuspend(&prev);
}
printf(".");
}
exit(0);
}
sigsuspend(&prev) unblock SIGCHLD, and pause until SIGNAL recieved.
if SIGNAL==SIGINT, sigint_handler executed,parent process wake up but cannot escape from root.
if SIGNAL==SIGCHLD, sigchld_handler executed, pid change, parent process wake up and block SIGCHLD and escape from root
int setjmp(jmp_buf env);
- returns multiple times. at first return 0, and next time returns second paremeter of longjmp, int retval.
void longjmp(jmp_buf env,int retval);
- no return, but if longjmp called trigger returns from most recently setjmp called jmp_buf "env"
#include "csapp.h"
jmp_buf buf;
int error1=0;
int error2=1;
void foo(void),bar(void);
int main(){
switch(setjmp(buf)){
case 0:
foo();
break;
case 1:
printf("Detected an error1 condition in foo\n");
break;
case 2:
printf("Detected an Error2 condition in foo\n");
break;
default:
printf("Unknown Error condition in foo\n");
}
exit(0);
}
void foo(void){
if(error1) longjmp(buf,1);
bar();
}
void bar(void){
if(error2) longjmp(buf,2);
}
above code makes:
Detected an Error2 condition in foo
first time -> returns 0 , call foo -> call bar -> error2, longjmp returns to setjmp(buf) with return code 2
if you deallocate a memory of data structure at the end of code, longjmp would make memory leak problem.
int sigsetjmp(sigjmp_buf env,int savemask);
int siglongjmp(sigjmp_buf env, int retval);
#include "csapp.h"
sigjmp_buf buf;
void handler(int sig){
siglongjmp(buf,1);
}
int main(){
if(sigsetjmp(buf,1)){
sio_puts("restarting\n");
}else{
Signal(SIGINT,handler);
sio_puts("starting\n");
}
while(1){
sleep(1);
sio_puts("processing...\n");
}
exit(0);//never reach here
}
starting
processing...
processing...
^Crestarting
processing...
processing...
processing...
^Crestarting
processing...
^Crestarting
processing...
1) signal handler installed
2) if SIGINT (CLT+C) recieved, signall hander -> siglongjmp to sigsetjmp
-->int savemask is flag, if savemask is 0, first SIGINT returns to sigsetjmp, but next time do not.
sigsetjmp() shall also save the current signal mask of the calling thread as part of the calling environment.
in other word, sigsetjmp saves the calling environment and signal context
strace -p {PID} // echo specific process' child processes
ps // list all process
top
pmap
Memory Allocation (0) | 2022.03.05 |
---|---|
Memory Virtualization (0) | 2022.02.12 |
Structure of Cache Memory (0) | 2022.02.07 |
디스크 저장장치 (0) | 2022.01.28 |
FLOAT와 DOUBLE (0) | 2021.07.04 |
댓글 영역