Filesystem on Unix / POSIX adrien.poteaux@univ-lille.fr CRIStAL, - - PowerPoint PPT Presentation

filesystem on unix posix
SMART_READER_LITE
LIVE PREVIEW

Filesystem on Unix / POSIX adrien.poteaux@univ-lille.fr CRIStAL, - - PowerPoint PPT Presentation

Filesystem on Unix / POSIX adrien.poteaux@univ-lille.fr CRIStAL, Universit Lille Year 2020-2021 This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. http://creativecommons.org/licenses/by-nc-sa/3.0/


slide-1
SLIDE 1

Filesystem on Unix / POSIX

adrien.poteaux@univ-lille.fr

CRIStAL, Université Lille

Year 2020-2021

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. http://creativecommons.org/licenses/by-nc-sa/3.0/

Process adrien.poteaux@univ-lille.fr Filesystem 1 / 55

slide-2
SLIDE 2

Normalisation of the interface

Unix:

Operating system, Ken Thompson and Denis Ritchie, Bell Labs, 1969, Source code distributed, Several versions. . .

POSIX:

Portable Open System Interface eXchange, Portable Open System Interface X for Unix, Standard IEEE, 1985, Standardized interface of what the system has to offer.

One POSIX function = one Unix system call.

adrien.poteaux@univ-lille.fr Filesystem 2 / 55

slide-3
SLIDE 3

System programmation: the C language is natural

A good programming language:

good semantic, efficient, access to all the computer structures (registers, bits. . . ), explicit allocation memory.

Natural interface with the system:

Libraries are written in C, One can use them from C

Other approaches are possible !

adrien.poteaux@univ-lille.fr Filesystem 3 / 55

slide-4
SLIDE 4

Libraries and system call

System call similar at using a library’s function. Standard C library: section 3 of man POSIX functions: section 2 of man Other differences:

No link edition, System code execution, Standard libraries are a higher level abstraction.

Important: one system call ≈ 1000 op. → minimize them !

adrien.poteaux@univ-lille.fr Filesystem 4 / 55

slide-5
SLIDE 5

Files: persistent memory

used to store data (“for good”), managed by the system: structure, naming, access, protection. . . filesystem is part of the OS. dissociation OS / Filesystem (e.g., Linux can mount NTFS) Unix approach: “everything” is a file

adrien.poteaux@univ-lille.fr Filesystem 5 / 55

slide-6
SLIDE 6

Filesystem: a graph organisation with nodes

root bin home

directory

tmp

poteaux

.. . link

file1 CS file2 slides-shell.tex ls cd rm file3 toto.txt

adrien.poteaux@univ-lille.fr Filesystem 6 / 55

slide-7
SLIDE 7

Operating on a file

Informations:

peripheric number, inode number file’s type, size. . .

  • dates. . .
  • wner, group, rights

Scanning the hierarchy: listing, moving in the hierarchy Changing the hierarchy:

Creation or destruction of nodes, Physical and symbolic links

Reading and writing in ordinary files.

adrien.poteaux@univ-lille.fr Filesystem 7 / 55

slide-8
SLIDE 8

Informations on a file

Structure struc stat ; access via:

# include <sys/types.h> # include <sys/stat.h> int stat(const char *path, struct stat *sb); int lstat(const char *path, struct stat *sb); # include <unistd.h> int fstat(int fd, struct stat *sb);

Identification of a node:

struct stat { dev_t st_dev; ino_t st_ino; ...

A lot more informations: cf lecture notes.

adrien.poteaux@univ-lille.fr Filesystem 8 / 55

slide-9
SLIDE 9

One example

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> struct stat sb; int status; status = stat(pathname, &sb); if (status) { perror("stat call"); return; } if (S_ISREG(sb.st_mode)) { printf("Ordinary file"); if (sb.st_mode & S_IXUSR) printf(", executable by its owner"); } (cf lecture notes to understand the different functions and macros)

adrien.poteaux@univ-lille.fr Filesystem 9 / 55

slide-10
SLIDE 10

Dealing with a file

Depending on the type of the file (first thing to check): Ordinary file: access to the data. Directory: access to the list of related nodes (“children”). Symbolic link: access to the name of the pointed file (then same as above). Special file: access to some peripheric data, possible limitations, specific operations might be possible.

adrien.poteaux@univ-lille.fr Filesystem 10 / 55

slide-11
SLIDE 11

Scanning directories

Content of a directory = linked list → loop on the list. Opening and closing a directory:

#include <dirent.h> DIR *opendir(const char *dirname); int closedir(DIR *dirp);

Loop on the input:

#include <dirent.h> struct dirent { ino_t d_ino; char d_name[]; struct dirent *readdir(DIR *dirp);

adrien.poteaux@univ-lille.fr Filesystem 11 / 55

slide-12
SLIDE 12

One example

static int lookup(const char *name) { DIR *dirp; struct dirent *dp; if ((dirp = opendir(".")) == NULL) { perror("couldn’t open ’.’"); return 0; } while ((dp = readdir(dirp))) { /* uses linked list */ if (! strcmp(dp->d_name, name)) { printf("found %s\n", name); closedir(dirp); return 1; } } if (errno != 0) /* cf man 3 errno for details */ perror("error reading directory"); else printf("failed to find %s\n", name); closedir(dirp); return 0; }

adrien.poteaux@univ-lille.fr Filesystem 12 / 55

slide-13
SLIDE 13

File pointed by a symbolic link

Path of the pointed file

#include <unistd.h> ssize_t readlink(const char *path, char *buf, size_t bufsize);

(returns the number of characters of the path ; −1 if error) Typical use:

char buf[PATH_MAX+1]; /* cf section 5.2.1 of lecture notes */ ssize_t len; if ((len = readlink(path, buf, PATH_MAX)) != -1) buf[len] = ’\0’; /* do not forget ! */ else perror("error reading symlink");

adrien.poteaux@univ-lille.fr Filesystem 13 / 55

slide-14
SLIDE 14

One can also...

create nodes (recursively if the directory does not exist), create links, destroy nodes, destroy directories, . . . (not detailed in this lecture)

adrien.poteaux@univ-lille.fr Filesystem 14 / 55

slide-15
SLIDE 15

Reading and writing in a file. . .

C language: the <stdio.h> library

FILE structure, FILE *fopen(const char *path, const char *mode); int fclose(FILE *fp); fprintf, fscanf,. . .

Fortran language:

  • pen(unit=FD,file=’filename’), close(FD)

write(FD,*), read(FD,*)

Behind both: system functions

<sys/types.h>, <sys/stat.h>, <fcntl.h>, <unistd.h> int open(const char *pathname, int flags, mode_t mode); int close(int fd); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);

adrien.poteaux@univ-lille.fr Filesystem 15 / 55

slide-16
SLIDE 16

File descriptor

A process need to use a file → designed by an integer File descriptor: index of a table containing informations related to the file. The first three elements are:

the standard input stdin (index 0) ; by default the keyboard, the standard output stdout (index 1) ; by default the screen, the error output stderr (index 2) ; also the screen by default.

<stdio.h> uses the same numbers (but one uses macros !) Fortran uses 5 (stdin) and 6 (stdout)

adrien.poteaux@univ-lille.fr Filesystem 16 / 55

slide-17
SLIDE 17

One example using directly system calls

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #define BUFSIZE 4096 static void copy_file(const char *src, const char *dst) { int fdsrc, fddst; char buffer[BUFSIZE]; int nchar; fdsrc = open(src, O_RDONLY); fddst = open(dst, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); while ((nchar = read(fdsrc, buffer, BUFSIZE))) { write(fddst, buffer, nchar); } close(fdsrc); close(fddst); }

(missing: error considerations)

adrien.poteaux@univ-lille.fr Filesystem 17 / 55

slide-18
SLIDE 18

Why libraries then ?

Higher level abstraction,

Formatted input / ouput fprintf(), fscanf(). . .

More efficient:

input and output use buffers, reduces costly system calls,

  • ne system call ≃ 1000 instructions,

call read / write only when the buffer is full / empty.

adrien.poteaux@univ-lille.fr Filesystem 18 / 55

slide-19
SLIDE 19

A better example

#include <stdio.h> #include <stdlib.h> #include <sys/stat.h> static void copy_file(const char *src, const char *dst) { struct stat stsrc, stdst; FILE *fsrc, *fdst; int c; lstat(src, &stsrc); lstat(dst, &stdst); if (stsrc.st_ino == stdst.st_ino && stsrc.st_dev == stdst.st_dev) { fprintf(stderr, "%s and %s are the same file\n", src, dst); return; } fsrc = fopen(src, "r"); fdst = fopen(dst, "w"); while ((c = fgetc(fsrc)) != EOF) fputc(c, fdst); fclose(fsrc); fclose(fdst); }

Warning: avoid mixing system calls and library calls (that uses system calls !)

adrien.poteaux@univ-lille.fr Filesystem 19 / 55

slide-20
SLIDE 20

The strace command

prints set of system calls made by a process, functions usually detailed in man (section 2), Default output is on stderr ! To separate the printing of the program and the one of strace: $ strace ./a.out 2>trace.txt You can run strace on the executable produced by the following code to see how <stdio.h> is using system calls

# include <stdio.h> int main() { int i; printf("Hello"); putchar(’\n’); printf("Hello again "); printf("world\n"); putchar(’H’); putchar(’e’); putchar(’l’); putchar(’l’); putchar(’o’); for (i=0;i<500;i++) putchar(’3’); putchar(’\n’); for (i=0;i<50;i++) { putchar(’2’); putchar(’\n’); } for (i=0;i<3000;i++) putchar(’p’); putchar(’\n’); return 0; }

adrien.poteaux@univ-lille.fr Filesystem 20 / 55

slide-21
SLIDE 21

The main function

int main(int argc, char *argv[], char *arge[]) argc gives the number of parameters (including the name of the command), argv provides the list of parameters (provided as strings ; functions as atoi might have to be used), argve is a list - ending by NULL of strings as varname=value providing environment variables (the one from the shell). Another way to get access to an environment variable: #include <stdlib.h> char *getenv(const char *name);

adrien.poteaux@univ-lille.fr Filesystem 21 / 55

slide-22
SLIDE 22

Process and program

What is a program ?

static code description, sequence of instructions.

What is a process ?

dynamic activity, it has a life: creation, execution, end.

One process = one execution of a program ; one can have:

several executions of programs, several executions of the same program, execution “at the same time” of different programs, execution “at the same time” of the same program.

File System adrien.poteaux@univ-lille.fr Process 22 / 55

slide-23
SLIDE 23

Processor

Physical entity, Affectation to a process:

time allocation, enables the process to progress.

Affectation choice: ordering of the process. . . dealt by the OS, We consider only a single processor, i.e. sequential operations:

time

adrien.poteaux@univ-lille.fr Process 23 / 55

slide-24
SLIDE 24

Process characteristics

Identifier #include <unistd.h> process ID pid_t getpid(); father’s process ID pid_t getppid(); Owner #include <unistd.h> True owner uid_t getuid(); gid_t getgid(); user that started the process uid_t geteuid(); gid_t getegid(); Inside a shell: several possible states: in background, foreground, stopped. . . job identifier (%1, %2, . . . )

adrien.poteaux@univ-lille.fr Process 24 / 55

slide-25
SLIDE 25

[poteaux@mathcalc04 ~]$ emacs slides-c.tex ^Z Suspended [poteaux@mathcalc04 ~]$ evince slides-c.pdf & [2] 10924 [poteaux@mathcalc04 ~]$ jobs [1] + Suspended emacs slides-c.tex [2]

  • Running

evince slides-c.pdf [poteaux@mathcalc04 ~]$ stop %2 [poteaux@mathcalc04 ~]$ bg %1 [1] emacs slides-c.tex & [2] + Suspended (Signal) evince slides-c.pdf [poteaux@mathcalc04 ~]$ jobs [1] Running emacs slides-c.tex [2] + Suspended (Signal) evince slides-c.pdf [poteaux@mathcalc04 ~]$ fg %2 evince slides-c.pdf ^C [poteaux@mathcalc04 ~]$ jobs [1] + Running emacs slides-c.tex [poteaux@mathcalc04 ~]$ kill %1 [poteaux@mathcalc04 ~]$ echo $SHELL /bin/bash [1] Ended 15 emacs slides-c.tex [poteaux@mathcalc04 ~]$ jobs [poteaux@mathcalc04 ~]$ # (example using tcsh)

adrien.poteaux@univ-lille.fr Process 25 / 55

slide-26
SLIDE 26

Process hierarchy

1 (init) 18706 (xterm) 18696 (xterm) 15242 (firefox) 15213 (xterm) 18698 (bash) 17325 (emacs) 18708 (bash) 15215 (bash) 22904 (ps)

adrien.poteaux@univ-lille.fr Process 26 / 55

slide-27
SLIDE 27

Starting a program

Two steps:

1

Creation of a new process by cloning the father: copy everything except its identity (pid),

2

Mutation to execute the new program,

No other way Cloning: use the fork() system call, Mutation: use the system call execve() ; there is a family of similar functions exec*()

adrien.poteaux@univ-lille.fr Process 27 / 55

slide-28
SLIDE 28

Cloning a process: the fork() system call

# include <unistd.h> pid_t fork(void); duplicates the current process, returns:

the pid of the child in the father’s process, 0 in the created process (child), −1 in case of error.

adrien.poteaux@univ-lille.fr Process 28 / 55

slide-29
SLIDE 29

One example

int main() { pid_t status; printf("[%d] I will generate a process\n", getpid()); status = fork(); switch (status) { case -1 : perror("Process creation"); exit(EXIT_FAILURE); case 0 : printf("[%d] I’m just born !\n", getpid()); printf("[%d] My father is %d\n", getpid(), getppid()); break; default: printf("[%d] I gave birth !\n", getpid()); printf("[%d] My child is %d\n", getpid(), status); } printf("[%d] I’m now ending\n", getpid()); exit(EXIT_SUCCESS); }

adrien.poteaux@univ-lille.fr Process 29 / 55

slide-30
SLIDE 30

Using it:

$ ./a.out [18727] I will generate a process [18728] I’m just born ! [18728] My father is 18727 [18728] I’m now ending [18727] I gave birth ! [18727] My child is 18728 [18727] I’m now ending

heap father process 18727 int status stack − → fork() data code

adrien.poteaux@univ-lille.fr Process 30 / 55

slide-31
SLIDE 31

Using it:

$ ./a.out [18727] I will generate a process [18728] I’m just born ! [18728] My father is 18727 [18728] I’m now ending [18727] I gave birth ! [18727] My child is 18728 [18727] I’m now ending

father process 18727 int status stack − → fork()

adrien.poteaux@univ-lille.fr Process 30 / 55

slide-32
SLIDE 32

Using it:

$ ./a.out [18727] I will generate a process [18728] I’m just born ! [18728] My father is 18727 [18728] I’m now ending [18727] I gave birth ! [18727] My child is 18728 [18727] I’m now ending

father process 18727 int status stack − → fork() child process 18728 int status − → fork()

adrien.poteaux@univ-lille.fr Process 30 / 55

slide-33
SLIDE 33

Using it:

$ ./a.out [18727] I will generate a process [18728] I’m just born ! [18728] My father is 18727 [18728] I’m now ending [18727] I gave birth ! [18727] My child is 18728 [18727] I’m now ending

father process 18727 int status 18728 stack − → fork() child process 18728 int status 0 − → fork()

adrien.poteaux@univ-lille.fr Process 30 / 55

slide-34
SLIDE 34

Using it:

$ ./a.out [18727] I will generate a process [18728] I’m just born ! [18728] My father is 18727 [18728] I’m now ending [18727] I gave birth ! [18727] My child is 18728 [18727] I’m now ending

This could be:

$ ./a.out [18727] I will generate a process [18727] I gave birth ! [18727] My child is 18728 [18727] I’m now ending [18728] I’m just born ! [18728] My father is 1 [18728] I’m now ending

“race” between the process: the printing order is random, if the father ends before the child, it is adopted by init (1).

Remark: the sleep() function can be useful to “force” the order.

adrien.poteaux@univ-lille.fr Process 31 / 55

slide-35
SLIDE 35

Inheritance

Memory of the child → copy of the father’s memory, Variables are not shared, Buffers of <stdio.h> are also copied ! Empty them before using fork ! Open descriptors are shared ! (more details later on)

adrien.poteaux@univ-lille.fr Process 32 / 55

slide-36
SLIDE 36

One example

int main() { pid_t status; printf("beginning "); status = fork(); switch (status) { case -1 : perror("Process creation"); exit(EXIT_FAILURE); case 0 : printf("[%d] child\n", getpid()); break; default: printf("[%d] father\n", getpid()); } printf("[%d] end\n", getpid()); exit(EXIT_SUCCESS); }

This gives:

$ ./a.out beginning [22485] father [22485] end beginning [22486] child [22486] end

beginning is written twice !

adrien.poteaux@univ-lille.fr Process 33 / 55

slide-37
SLIDE 37

Ending children

Use exit(), The father can know the returned value (success or not. . . ) Waiting a child: #include <sys/wait.h> pid_t wait(int *pstatus);

returns the pid of the child, −1 if error (no child. . . ), suspends the process if no child ends, *pstatus provides informations on the way the child ended.

adrien.poteaux@univ-lille.fr Process 34 / 55

slide-38
SLIDE 38

Informations on the child ending

stored in the integer status pointed by par pstatus: macro meaning WIFEXITED(status) process used exit() WIFSIGNALED(status) process received a signal WIFSTOPPED(status) process has been stopped returned value: if WIFEXITED(status) only !

  • n 8 bits,

access with WEXITSTATUS(status)

Signal that made the ending / stop of the process:

if WIFSIGNALED(status) or WIFSTOPPED(status), access with WTERMSIG(status) or WSTOPSIG(status)

adrien.poteaux@univ-lille.fr Process 35 / 55

slide-39
SLIDE 39

One example

int main(int argc, char *argv[]) { int status; pid_t pid, pidz; switch (pid = fork()) { case -1 : perror("Process creation"); exit(EXIT_FAILURE); case 0 : printf("[%d] Child dying\n", getpid()); exit(2); default: printf("[%d] Father created %d\n", getpid(), pid); pidz = wait(&status); if (WIFEXITED(status)) printf("[%d] My child %d ended normally,\n" "[%d] returned value: %d\n", getpid(), pidz, getpid(), WEXITSTATUS(status)); else printf("[%d] My child ended in a bad way\n", getpid()); } exit(EXIT_SUCCESS); } $ ./a.out [22886] Father created 22887 [22887] Child dying [22886] My child 22887 ended normally, [22886] returned value: 2

adrien.poteaux@univ-lille.fr Process 36 / 55

slide-40
SLIDE 40

Waiting a specific child

#include <sys/wait.h> pid_t waitpid(pid_t pid, int *pstatus, int options); pid is the child we want to wait, pid = -1 for any child,

  • ptions: binary combination of WNOHANG (non blocking call)

and WUNTRACED (stopped process)

adrien.poteaux@univ-lille.fr Process 37 / 55

slide-41
SLIDE 41

Zombie process

Process ended but not yet waited by its father, Avoid them: system keeps its formations for the father (memory usage), One solution: double fork (not seen here).

adrien.poteaux@univ-lille.fr Process 38 / 55

slide-42
SLIDE 42

Mutation of a process

#include <unistd.h> int execve(const char *filename, char *const argv[], char *const envp[]); Execute the program filename,

The process ends at the end of the program execution, If no error, no return to the current program !

parameters argv[] environnement variables envp[] (similar to the main() function in C)

  • ther functions (that use execve - make the call easier):

execl, execv, execle, execlp, execvp.

adrien.poteaux@univ-lille.fr Process 39 / 55

slide-43
SLIDE 43

How the shell starts a new process

$ commmand

fork() wait() exec(command) exit() (fpid) sh (fpid) sh (fpid) sh (cpid) sh (cpid) command

adrien.poteaux@univ-lille.fr Process 40 / 55

slide-44
SLIDE 44

Executing a command in background

$ commmand &

fork() wait() exec(command) exit() (fpid) sh (fpid) sh (cpid) sh (cpid) command

The shell does not wait the process anymore !

adrien.poteaux@univ-lille.fr Process 41 / 55

slide-45
SLIDE 45

Conditional execution

$ cmd1 && cmd2

fork() wait() exec(cmd1) val exit() (fpid) sh (fpid) sh fpid sh (cpid1) sh (cpid1) cmd1 fork() wait() exec(cmd2) exit() (fpid) sh (cpid2) sh (cpid2) cmd2 (fpid) sh

adrien.poteaux@univ-lille.fr Process 42 / 55

slide-46
SLIDE 46

Redirection of input/output

int dup2(int oldfd, int newfd); makes newfd be the copy of oldfd, closes newfd first if necessary. One example:

# include <stdio.h> # include <stdlib.h> # include <unistd.h> # include <fcntl.h> # include <sys/stat.h> static void print_inode(const char *str, int fd) { struct stat st; fstat(fd, &st); fprintf(stderr, "\t%s : inode %d\n", str, st.st_ino); }

adrien.poteaux@univ-lille.fr Process 43 / 55

slide-47
SLIDE 47

Example, part 2

int main(int argc, char *argv[]) { int fdin, fdout; fdin = open(argv[1], O_RDONLY); fdout = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); fprintf(stderr, "Before dup2() :\n"); print_inode("fdin", fdin); print_inode("fdout", fdout); print_inode("stdin", STDIN_FILENO); print_inode("stdout", STDOUT_FILENO); dup2(fdin, STDIN_FILENO); dup2(fdout, STDOUT_FILENO); fprintf(stderr, "After dup2() :\n"); print_inode("fdin", fdin); print_inode("fdout", fdout); print_inode("stdin", STDIN_FILENO); print_inode("stdout", STDOUT_FILENO); exit(EXIT_SUCCESS); } $ ./a.out file1.txt file2.txt Before dup2() : fdin : inode 2624503 fdout : inode 2624504 stdin : inode 3 stdout : inode 3 After dup2() : fdin : inode 2624503 fdout : inode 2624504 stdin : inode 2624503 stdout : inode 2624504

adrien.poteaux@univ-lille.fr Process 44 / 55

slide-48
SLIDE 48

How the shell works:

$ commmand > file

fork() wait() fd=open(file) dup2() exec(command) exit() (fpid) sh (fpid) sh stdout (cpid) sh stdout sh stdout sh (fpid) sh (cpid) command tty file fd stdout

adrien.poteaux@univ-lille.fr Process 45 / 55

slide-49
SLIDE 49

Signals: interruptions

Sent to a process. . . by another process or the system. Dealing with signals: handlers (function called when receiving the signal) One example: shell managing jobbs (bg, fg. . . ).

adrien.poteaux@univ-lille.fr Process 46 / 55

slide-50
SLIDE 50

Some signals

name event behaviour SIGINT interruption <intr>, C-c terminates SIGQUIT interruption <quit>, C-\ terminates + core SIGKILL immediate ending terminates (unchangeable) SIGTERM ending terminates SIGTSTP suspension <susp>, C-z suspended SIGSTOP suspension signal suspended (unchangeable) SIGCONT a stopped process continues restarts (if stopped !)

adrien.poteaux@univ-lille.fr Process 47 / 55

slide-51
SLIDE 51

Programming signals

A process sends a signal to another: # include <signal.h> int kill(pid_t pid, int sig);

Rights needed, returns −1 in case of error, sig= 0 means no signal (testing the pid).

Waiting a signal # include <unistd.h> int pause(void);

blocks the process until it receives a signal, then acts according to the sent signal.

adrien.poteaux@univ-lille.fr Process 48 / 55

slide-52
SLIDE 52

Handling signals

Handler executed by the signal target, function’s prototype:

void handler(int signum);

function’s type:

void (*phandler)(int);

Structure struct sigaction

struct sigaction { void (*sa_handler)(); sigset_t sa_mask; int sa_flags; };

Primitive sigaction()

# include <signal.h> int sigaction (int sig, const struct sigaction *new, struct sigaction *old);

adrien.poteaux@univ-lille.fr Process 49 / 55

slide-53
SLIDE 53

One example

# include <stdio.h> # include <stdlib.h> # include <signal.h> # define NSIGMAX 5 static void set_default() { struct sigaction sa; sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT,&sa, (struct sigaction *) 0); } static void int_handler(int sig) { static int nsig = 0; if (nsig++ < NSIGMAX) printf(" C-c won’t kill me\n"); else { printf(" unless you insist...\n"); set_default(); } } int main() { struct sigaction sa; sa.sa_handler = int_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, (struct sigaction *) 0); for(;;) pause(); fprintf(stderr, "bye\n"); exit(EXIT_SUCCESS); } $ ./a.out ^C C-c won’t kill me ^C C-c won’t kill me ^C C-c won’t kill me ^C C-c won’t kill me ^C C-c won’t kill me ^C unless you insist... ^C $

adrien.poteaux@univ-lille.fr Process 50 / 55

slide-54
SLIDE 54

Child ending

Father receives the SIGCHLD signal when a child ends, By default: ignored Specific treatment:

eliminating a zombi process, wait() or waitpid() call.

Father also warned when a child is suspendend (also ignored by default).

adrien.poteaux@univ-lille.fr Process 51 / 55

slide-55
SLIDE 55

Communication between process: pipe

Needed communication between process:

Two process cooperating, Combining the results of two or more process.

Several possible communications:

exit() & wait(): returned value only. kill(): communicating a signal. write() & read() → with an intermediate file.

Another (better) way: pipes

Communication between reader(s) and writer(s), No intermediate file necessary !

ABC

cat file | more

adrien.poteaux@univ-lille.fr Process 52 / 55

slide-56
SLIDE 56

Anonymous pipe

One file without any name

not in the filesystem data, cannot use open()

Creating a pipe:

# include <unistd.h> int pipe(int *fd)

Creates two descriptors:

fd[0] for reading, fd[1] for writing. returns 0 (success) or −1 (error), closing a descriptor ? no more access, inheritance of descriptors after a fork()

ABC

fd[0] fd[1]

adrien.poteaux@univ-lille.fr Process 53 / 55

slide-57
SLIDE 57

Using pipes

Typical use:

  • ne process writes,

another reads.

How to do that ?

A pipe is created in a parent process, Father forks to create a child, Each process have access to the descriptors Each uses one end of the pipe (thus closes the useless one)

ABC

fork() == 0 fd[0] /* close(fd[1]) */ fork() != 0 fd[1] /* close(fd[0]) */

adrien.poteaux@univ-lille.fr Process 54 / 55

slide-58
SLIDE 58

One example: the shell

$ cmd1 | cmd2

pipe() fork() fork() wait()

close(fd[0]) close(fd[1])

dup2() dup2() exec(cmd1) exec(cmd2) exit() exit() (fpid) sh sh(fpid) stdout stdin (cpid1) sh stdout (cpid2) sh stdin sh sh stdout sh stdin sh (fpid) sh (cpid1) cmd1 (cpid2) cmd2 tty fd fd[1] fd[0] stdout stdin

close(fd[0]) close(fd[1]) adrien.poteaux@univ-lille.fr Process 55 / 55