class note 2

advertisement
Outline
CS4233 Network Programming
z
Beginning of UNIX programming
z
UNIX Programming
z
z
UNIX APIs
z
Chen-Lung Chan
Department of Computer Science
National Tsing Hua University
z
z
z
z
z
z
Compiler
z
Assembler
z
Assembly code
Linker
Fully-resolved object
code (machine code)
z
z
Loader
Executable image
Create/wait a thread
Thread synchronization
C Compilers
Source code
Object code
(machine code)
File I/O
Process management
Signal handling
Interprocess communications
POSIX threads
z
Compilers: The Big picture
gcc
make
gcc, cc
Ex1: build an execution file “a.out” from hw1.c
$ gcc hw1.c
Ex2: build an execution file “hw1” from hw1.c
$ gcc –o hw1 hw1.c
Ex3: link with a library (/usr/lib/libxnet.so)
$ gcc –o hw1 hw1.c –lxnet
Ex4: compile an object file (hw1.c ⇒ hw1.o)
$ gcc –c hw1.c
Exercise
z Write
a “Hello World” UNIX console
program.
make (1/3)
z
make [-f makefile][option] target
z
z
$ gcc –o hello hello.c
z $ ./hello
Hello World!!
z
z
A tool to update files derived from other files
The default files for make
are ./makefile, ./Makefile, ./s.makefile
Use the –f option to specify some other file
z
z
The makefile has three components
z
z
z
make (2/3)
z
Macros:
z
z
z
string1 = string2
Ex:
CC = gcc
CFLAG = -I../include
Target rules:
z
z
Target: [prerequisite…]
<tab> command
Ex:
hw1: myprog1.c myprog2.c myprog3.c
<tab> $(CC) –o hw1 $(CFLAG) myprog1.c myprog2.c myprog3.c
$ make –f makefile1
Macros: define constants
Target rules: specify how targets are made
Inference rules: specify how targets can be made,
implicitly.
make (3/3)
z
Reference
z
Ex:
CC = gcc
hw: main.o util.o
$(CC) –o hw main.o util.o
util.o: util.h util.c
$(CC) –c util.c
main.o: main.c
$(CC) –c main.c
clean:
@rm *.o *~
z
http://www.study-area.org/cyril/opentools/opentools/makefile.html
Portability
z Standards
Byte Order
z
z
Source code portability: ANSI/ISO C
z UNIX standards: POSIX, open group
z Internet engineering task force (IETF)
z
z 32
bit vs 64 bit
z Byte order
z
Standard programming language
z
z
z
z
z
High-order byte is
stored at lowest
address
Outline
z
Beginning of UNIX programming
z
z
z
z
z
z
z
gcc
make
UNIX APIs
z
Example: POSIX.1
Auto-configuration mechanisms
Programmer discipline
Low-order byte is
stored at lowest
address
Big-Endian
z
Example: ANSI/ISO C
Standard libraries
Standard API to operating system
z
z
Little endian vs big endian
Improve Source Code Portability
z
Little-Endian
File I/O
Process management
Signal handling
Interprocess communications
POSIX threads
z
z
Create/wait a thread
Thread synchronization
UNIX File System
z inode:
a segment of data in a filesystem
that describes a file, including how to find
the rest of the file in the system
z File descriptor: a non-negative integer,
with a per-process mapping to an open
file description
z Open file description: an OS internal
data-structure, shareable between
processes
File Descriptors (2/2)
File Descriptors (1/2)
z
Each open file is associated with an open file
description.
z
z
Each process has a (logical) array of references to
open file descriptions.
Logical indices into this array are file descriptors.
z
z
These integer values are used to identify the files for I/O
operations.
The file descriptor 0 is reserved for standard input,
the file descriptor 1 for standard output, and the file
descriptor 2 for the standard error.
Direct File I/O (1/2)
z Functions
z
open(), creat(), read(), write(), lseek(),
close(), dup()
z When
to use direct file I/O?
Do not want to view the data as characters
z Want greater efficiency
z The extra (stream) layer of buffering causes
us problems with synchronization
z
Direct File I/O (2/2)
z
z
z
int open(const char *pathname, int flags)
int open(const char *pathname, int flags, mode_t
mode)
int creat(const char *pathname, mode_t mode)
z
z
z
z
z
z
z
z
z
write to a file descriptor
z
reposition read/write file offset
z
z
close a file descriptor
Process Management
z Environment
variables
z
status
z Process ID
z Functions
fork()
z exit()
z wait() and waitpid()
z execv()
Create/wait a thread
Thread synchronization
Environment Variables
z Exit
z
File I/O
Process management
Signal handling
Interprocess communications
POSIX threads
z
int close(int fd)
gcc
make
UNIX APIs
z
read from a file descriptor
off_t lseek(int fildes, off_t offset, int whence)
Beginning of UNIX programming
z
ssize_t write(int fd, const void *buf, size_t count)
z
z
z
ssize_t read(int fd, void *buf, size_t count)
z
z
open and possibly create a file or device
Outline
A set of environment variables is associated
with each process.
z
z
z
Ex:
$ EDITOR=/usr/bin/joe
$ export EDITOR
These values are passed to the process when
it is executed.
A process can access its environment
variables via getenv().
Exit Status
z
Each process (except the system's initial
process) has a parent process
z
When a process terminates, its parent can find out
what caused the termination via a status value.
z
z
z
The termination status contains a short integer exit status
code.
The function exit() allows a process to terminate itself and
specify the exit status code that it wants to return to the
parent.
z Each
process has a unique identifier, of
signed arithmetic type pid_t.
z The process ID of the current process
can be obtained by calling the function
getpid().
z ps - report process status
When a command is run from a shell, the exit
status code is returned to the shell, which allows it
to be used in job control statements.
fork()
z Create
a new process by duplicating the
context of the calling process.
z The calling process is called the parent,
and the new process the child.
z The return value of fork() distinguishes
the two processes.
Child: 0
z Parent: child process ID, or -1 for error
z
Process ID
exit() and waitpid()
z
exit(int status)
z
z
z
z
z
Clean up the process (for example, close all files)
Tell its parent that it is dying (SIGCHLD)
Tell child processes that it is dying (SIGHUP)
status can be accessed by the parent
waitpid(pid_t pid, int *stat_loc, int options)
z
waitpid() suspends the calling process until one of
its children changes state.
z
z
Return the pid and status of the child process
Ex: waitpid(-1, &status, WCONTINUED);
What Happens to a Process Whose
Parent Does not Wait for It?
z
z
z
The process becomes a zombie in UNIX
terminology.
Zombies stay in the system until they are
waited for.
If a parent terminates without waiting for a
child, the child becomes an orphan and is
adopted by the system init process which has
process ID equal to 1.
z
Comparison of a foreground process and a daemon
process … cont’d
z
Foreground process (will not return to the shell)
z
z
z
Daemon
z
z
$ ./run
test!!
test!!
…
Daemon (run in the background mode, return to the shell
immediately)
z
$ ./run
$
test!!
test!!
…
A collection of background processes that perform a particular
system task.
Comparison of a foreground process and a daemon
process
z
The init process periodically waits for children so
eventually orphan zombies are removed.
Daemon (2/4)
z
Daemon (1/4)
run.c:
int main()
{
while (1)
{
printf(“test!!\n”);
sleep(10);
}
}
Daemon (3/4)
z How
to develop a daemon?
if (fork())
exit(0);
z setsid(): run a program in a new session
z Then, the parent process will terminate
immediately, and the child process
becomes a daemon process.
z
Daemon (4/4)
z
run_daemon.c:
execv()
z
int main()
{
if (fork())
return 0;
setsid();
while (1)
{
printf(“test!!\n”);
sleep(10);
}
}
Outline
z
Beginning of UNIX programming
z
z
z
z
z
z
z
File I/O
Process management
Signal handling
Interprocess communications
POSIX threads
z
z
Create/wait a thread
Thread synchronization
Execute a command
z
Wipes out most of the context
z
z
z
z
The file descriptor table is kept
We can manipulate the I/O of the command by
manipulating the file descriptor table.
Anything after this system call will not be executed
if the system call is successful.
Ex:
char *argv[3] = {“ps”, “-e”, NULL};
execv(“/bin/ps”, argv);
Signals
z
z
UNIX APIs
z
z
gcc
make
exec family system calls
Software interrupts
A notification to a process that an event has
occurred
z
z
Signals can be sent by:
z
z
z
usually the process doesn’t know ahead of time
exactly when a signal will occur
a process to another process (or to itself)
the kernel to a process
Every signal has a name specified in
<signal.h>.
Signals Generation (1/2)
z Some
examples of how signals are
generated
z
z
Signals Generation (2/2)
z Some
examples of how signals are
generated … cont’d
The kill command can be used to send signals.
Certain terminal characters generate signals
z an interrupt character (<ctrl>-c or Delete)
generates a signal called SIGINT
z a quit character (<ctrl>-backslash)
z
z
z
z
z
terminates a process
generates a signal called SIGQUIT to process and
generates a core image of the process
The core image can be used for debugging
z
SIGHUP
z
z
The terminal disconnects from the system
Ask a daemon to read configuration files again
z
SIGCHLD
z
SIGKILL
z
z
z
z
z
z
A child process changes its status (ex: terminate)
A process can provided a function that is
called whenever a specific type of signal
occurs.
z
Destroy the process
kill -9 <pid>
Software termination
The default signal sent by the kill command
z
SIGINT
z
SIGALRM
z
z
When user presses the interrupt key (Ctrl-C)
Alarm clock
SIGURG signal is generated when some urgent
data arrives on a socket
Handle a Signal (1/2)
SIGTERM
z
floating point arithmetic errors generate a signal
called SIGFPE
Certain software conditions can generate
signals
z
z
Some of the Signals
Certain hardware conditions generate
signals
z
This function, called a signal handler, can do
whatever the process wants to handle the condition.
(“catch” the signal)
A process can choose to ignore the signal. All
signals, except SIGKILL, can be ignored.
z
SIGKILL is special, it guarantees the system
administrator a way of terminating any process
Handle a Signal (2/2)
zA
process can allow the default to
happen.
z
How to Specify the Signal Handler?
z
Use signal() system call
i.e. #include <signal.h>
z signal() is a function that returns a pointer to a function that
returns an integer.
z The function argument specifies the address of the function
that doesn’t return anything.
z There are two special cases that provide special values for
the function argument
Normally, a process is terminated on receipt
of a signal with certain signals generating a
core image of the process in the current
working directory.
z
z
z
ANSI signal()
z Syntax:
#include <signal.h>
void (*signal(int sig, void (*disp)(int)))(int)
z
Semantics
sig - signal (defined in signal.h)
z disp - SIG_IGN, SIG_DFL or the address of a
signal handler
z Handler may be erased after one invocation
SIG_DFL to specify the signal is to be handled in the default way
SIG_IGN to specify that the signal is to be ignored.
If the signal handler returns, the process that received
the signal continues where it was interrupted.
Example
z
Catch SIGTERM
z
Ex:
void destroy(int sig)
{
fprintf(stderr, "destroy!!\n");
}
z
Use the kill command to send SIGTERM to the process
z
z
Simple techniques for avoiding this can lead to
incorrect results.
int main()
{
signal(SIGTERM, destroy);
sleep(60);
}
kill()
Outline
z Send
a signal to a process
#include <signal.h>
z #include <sys/types.h>
z int kill(pid_t pid, int signo)
z
Beginning of UNIX programming
z
z
z
z
UNIX APIs
z
z
z
z
z
z
z
module
Communication of
modules in a
process
z
module
z
z
z
File I/O
Process management
Signal handling
Interprocess communications
POSIX threads
z
IPC on a Single process
gcc
make
Create/wait a thread
Thread synchronization
IPC on a Single Computer
Process A
Process B
global variables
function calls
parameters
result
Kernel
IPC on Two Computers
Process A
Several Different Methods of IPC
Process B
DOS (Distributed OS)
Kernel
Kernel
Shared Memory (1/2)
z
shmget() - get shared memory segment identifier
z
int shmget(key_t key, size_t size, int shmflg)
z
ex: a 4-byte shared memory with key = 99887
z
z
z
z
z
(Software Interrupt)
z Pipes and FIFOs
z Message Queues
z Semaphores
z Shared Memory
z Sockets
z RPC (Remote Procedure Calls)
z Mutex and Conditional Variables
Shared Memory (2/2)
z
Ex: allocate/release a block of shared memory
(for an int variable)
z
shmget((key_t)99887, 4, SHM_R | SHM_W | IPC_CREAT); // create
shmget((key_t)99887, 4, SHM_R | SHM_W);
// open
Other operations
z
z Signals
shmat() - attaches the shared memory
shmdt() - detaches from the shared memory
z
z
z
ipcs - provide information on ipc facilities
ipcrm - remove a message queue, semaphore set or shared
memory id
int shmid = shmget(33537, sizeof(int), SHM_R | SHM_W |
IPC_CREAT);
Attach:
z
int *shmptr = (int *)shmat(shmid, (char *)0, 0);
z
Access: (directly access the shm via the pointer)
z
Detach:
z
Release:
Related commands
z
Allocate:
z
z
z
*shmptr = 1234;
shmdt( (void *)shmptr);
shmctl(shmid, IPC_RMID, NULL);
Semaphores
z is
a synchronization primitive
z is a form of IPC, semaphore is not used
for exchanging large amounts of data, as
is pipe; but are intended to let multiple
processes synchronize their operations.
Semaphores in System V (1/3)
z
Get set of semaphores
z
int semget(key_t key, int nsems, int semflg)
z
z
z
z
z
Semaphore operations … cont’d
z
z
struct sembuf {
ushort_t sem_num;
short
sem_op;
short
sem_flg;
}
z
struct sembuf sems[1];
// … initialize sems[1]
semop(semid, sems, 1);
Ex1: wait the semaphore
sems[0].sem_num = 0;
sems[0].sem_op = -1;
sems[0].sem_flg = 0;
semop(semid, sems, 1);
/* semaphore # */
/* semaphore operation */
/* operation flags */
User semaphore template for semop() system calls
Semaphores in System V (3/3)
z
Semaphore operations … cont’d
z
int semop(int semid, struct sembuf *sops, size_t
nsops)
z
semget((key_t)99667, 1, SEM_R | SEM_A | IPC_CREAT); // create
semget((key_t)99667, 1, SEM_R | SEM_A);
// open
Semaphore operations
z
Semaphores in System V (2/3)
Ex: a semaphore with key = 99667
z
Semaphore control operations
z
z
z
Ex2: signal the semaphore
sems[0].sem_num = 0;
sems[0].sem_op = 1;
sems[0].sem_flg = 0;
semop(semid, sems, 1);
int semctl(int semid, int semnum, int cmd, ...)
Ex: release a semaphore
semctl(semid, 0, IPC_RMID);
Notes: the default value of a semaphore is 0.
z
z
Wait for such a semaphore will cause the program being blocked.
Use semctl() or semop() to set the value of a semaphore to a positive
value before waiting for it.
Summary
z Use
ipcs to check if IPC resources are
created successfully.
z Use ipcrm to remove IPC resources
which are not released successfully.
z Choose a IPC key which does not
conflict with others.
z
Based on your student number
z Be
z
A thread is an independent sequence of
execution of program code inside a process.
A thread is often called a lightweight process.
z
But it is NOT a process
z
z
Beginning of UNIX programming
z
z
z
z
z
z
z
z
A thread has less
z
Program counter, a stack, and a set of registers, etc.
File I/O
Process management
Signal handling
Interprocess communications
POSIX threads
z
z
Create/wait a thread
Thread synchronization
Threads (2/2)
z
Threads share
z
z
It is something smaller than a process.
Memory, instruction, program counter, stack pointer,
registers, open file descriptor, etc.
gcc
make
UNIX APIs
z
z
Process instructions, most data, open files, signal
handlers and signal dispositions, current working
directory, user and group ID
Threads do not share
The process context includes
z
z
z
careful while using pointers!
Threads (1/2)
z
Outline
Thread ID, set of registers (including pc – program
counter – and sp – stack pointer), stack, errno,
signal mask, priority
POSIX standard: pthreads
z
Specifies the API and the way other POSIX
interfaces deal with threads
Threads vs Processes
z Advantages
of threads
It is less expensive to create a thread.
z Inter-thread communication is much simpler
and cheaper than inter-process
communication.
z
z Thread
Basic Thread Functions (1/2)
z
Pthread library
z
Thread creation
z
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void * arg)
z Thread attributes can be modified
z
z
programming can be dangerous
z
z
Wait for a thread to terminate
int pthread_join(pthread_t thread, void **value_ptr)
z Note that the thread ID, and not a pointer to it, is given as the first
argument.
z The return value of the function executed by the thread is stored in
*value_ptr.
z
Ex: Create a thread and wait for its termination
z
void* thread_proc(void *pv)
{
…
}
pthread_t th;
pthread_create(&th, NULL, thread_proc, NULL);
…
pthread_join(th, NULL);
z
z
Use NULL for default
Returns 0 if successful, and an error number if it fails
New thread executes the function given by start_routine
z
It is easy to make mistakes.
z Some system functions may break with
threads.
z
Basic Thread Functions (2/2)
Append “-lpthread” while building a pthread project
void* thread_proc(void *pv)
arg is the argument to this function
The thread ID is stored in *thread
Some More Thread Functions
z
pthread_t pthread_self(void)
z
z
void pthread_exit(void *value_ptr)
z
z
Get ID of the current thread
Exits a thread
int pthread_detach(pthread_t tid)
z
Make a thread detachable
z
z
This guarantees that the memory resources consumed by
the thread will be freed immediately when the thread
terminates.
This prevents other threads from synchronizing on the
termination of the thread using pthread_join().
Mutual Exclusion
z
z
z
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutex-attr_t *mutexattr)
z
z
z
Mutex pointer and mutex attribute pointer arguments
int pthread_mutex_destroy(pthread_mutex_t *mutex)
Ex:
z
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
// … critical section
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);
Condition Variables
z
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex)
z
z
z
int pthread_cond_signal(pthread_cond_t *cond)
z
z
z
z
Atomically releases mutex and blocks on cond
On return, mutex is locked and owned by the calling thread
Unblocks one of the threads blocked on cond
The unblocked threads contend for their mutex
int pthread_cond_init(pthread_cond_t *cond,
pthread_condattr_t *cond_attr)
int pthread_cond_destroy(pthread_cond_t *cond)
Download