500 likes | 696 Views
Thực hành Unix, Linux (3) Ngô Thanh Nguyên Bộ môn Hệ thống và Mạng máy tính Khoa Khoa học và kỹ thuật máy tính Email: ntnguyen@cse.hcmut.edu.vn. Lập trình trên Linux. Lập trình IPC Dùng pipe Dùng signal Dùng semaphore.
E N D
Thực hành Unix, Linux (3) Ngô Thanh Nguyên Bộ môn Hệ thống và Mạng máy tính Khoa Khoa học và kỹ thuật máy tính Email: ntnguyen@cse.hcmut.edu.vn Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình trên Linux • Lập trình IPC • Dùng pipe • Dùng signal • Dùng semaphore Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình trên Linux • Lậptrình IPC • Dùng pipe • Dùng signal • Dùng semaphore Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Giới thiệu về IPC • Mục tiêu của IPC • IPC: Inter-Process Communication • Cho phép phối hợp hoạt động giữa các quátrình trong hệthống • Giải quyết đụng độtrên vùng tranh chấp • Truyền thông điệp từquátrình này đến các quátrình khác • Chia sẻ thông tin giữa các quá trình với nhau Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Giao tiếp và đồng bộ Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình trên Linux • Lậptrình IPC • Dùng pipe • Dùng signal • Dùng semaphore Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Giao tiếp thông qua PIPE • Là kênh truyền dữ liệu giữa các process với nhau theo dạng FIFO Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Các tác vụ trên pipe • Write: #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count) • Read: #include <unistd.h> ssize_t read(int fd, const void *buf, size_t count) Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hai loại pipe • Unnamed pipe • có ý nghĩa cục bộ • chỉ dành cho các process có quan hệ bố con với nhau • Named pipe (còn gọi là FIFO) • có ý nghĩa toàn cục • có thể sử dụng cho các process không liên quan bố con Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Unnamed pipe • Tạo unnamed pipe: #include <unistd.h> int pipe(int filedes[2]); • Kết quả • Thành công, kết quảthực thi hàm pipe() là0, có hai file descriptor tương ứng sẽ được trảvềtrong filedes[0], filedes[1] • Thất bại: hàm pipe() trả về -1, mã lỗi trong biến ngoại errno Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Unnamed pipe (2) • Duplex • Linux: unidirectional/half-duplex, i.e. filedes[0] chỉ được dùng để đọc còn filedes[1] chỉ được dùng đểghi dữliệu • Solaris: full-duplex, i.e. nếu ghi vào filedes[0], thì filedes[1] được dùng để đọc và ngược lại Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ về unnamed pipe #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { intfp[2]; char s1[BUFSIZ]; char s2[BUFSIZ]; pipe(fp); if (fork()==0) { /* Child Read */ printf("\nInput: "); fgets(s1, BUFSIZ, stdin); s1[strlen(s1)]=0; close(fp[0]); write(fp[1], s1, strlen(s1)+1); } else { /* Parent Write */ close(fp[1]); read(fp[0], s2, BUFSIZ); printf("\nFrom pipe>%s\n", s2); } return 0; } Dịch, thựcthi $gccunnamedpipe.c -o unnamedpipe $./unnamedpipe Input: I Love Penguin From pipe>I Love Penguin $ Khoa KH&KTMT - ĐạihọcBáchKhoa Tp. HCM
Dùng pipe để tái định hướng • Pipe cóthể được dùng đểkết nối các lệnh với nhau (do chương trình shell thực hiện) • Vídụ: $ps -ef | grep a01 | sort $ls | more • Đối với chương trình người dùng, cóthểdùng một trong hai system call sau kết hợp với pipe đểthực hiện: • dup() • dup2() Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
dup() #include <unistd.h> int dup(intoldfd); • Chức năng: tạo bản sao của descriptor oldfd.File descriptor nhỏnhất còn chưa sửdụng sẽlàbản sao của oldfd. • Kếtquảcủahàm dup() • Thànhcông: file descriptor bảnsao • Lỗi: -1 (mãlỗitrongbiếnngoạierrno) • Filedescriptor mới cónhững thuộc tính chung với file ban đầu • Cùng chỉ đến một file (hay pipe) đang mở • Chiasẻcùngmột file pointer • Cócùng một chế độtruy cập(read, write hoặcread/write) Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
dup2() #include <unistd.h> int dup2(int oldfd, int newfd); • Tương tự dup(), nhưng dup2() cho phép chỉ định file descriptor đích • Descriptornewfdtrỏ đến cùng một filenhưoldfd Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ file dup.c #include <unistd.h> int main() { // ps -ef | sort | grep int pipe1[2], pipe2[2]; pipe(pipe1); if (fork()) { /* Parent */ pipe(pipe2); if(fork()) { /* Parent */ close(0); // Close standard input dup(pipe2[0]); // standard input -> Read Pipe2 close(pipe1[0]); close(pipe1[1]); close(pipe2[0]); close(pipe2[1]); execl("/bin/grep", "grep", "ps", NULL); } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (2) else { /* Child */ close(0); // Close standard input dup(pipe1[0]); // standard input -> Read Pipe1 close(1); // Close standard output dup(pipe2[1]); // standard output -> Write Pipe2 close(pipe1[0]); close(pipe1[1]); close(pipe2[0]); close(pipe2[1]); execl("/bin/sort", "sort", NULL); } } else { /* Parent */ close(1); // Close standard output dup(pipe1[1]); // standard output -> Write Pipe1 close(pipe1[0]); close(pipe1[1]); execl("/bin/ps", "ps", "-ef", NULL); } exit(0); } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Named pipe • Tương tự như unnamed pipe • Một sốtính năng cần chúý: • Được ghi nhận trên file system (directory entry, file permission) • Có thể dùng với các process không có quan hệ bố con • Có thể tạo ra từ dấu nhắc lệnh shell (bằng lệnh mknod) Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Tạo named pipe - mknod() • System call: #include <sys/types.h> #include <sys/stat.h> intmknod(const char *path, mode_t mode, dev_tdev); • Trong đó • path: đường dẫn đến pipe (trên file system) • mode: quyềntruycậptrên file = S_IFIFO kếthợpvớitrịkhác • dev: dùnggiátrị 0 • C/C++ library call #include <sys/types.h> #include <sys/stat.h> intmkfifo(const char *pathname, mode_t mode); Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ Dịchvàthựcthi $gccfifo.c -o fifo $./fifo Parent inputs string and write to FIFO1: Test1 Child read from FIFO1: Test1 Input string from child to feedback: Test2 Feedback data from FIFO2: Test2 fifo.c #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/errno.h> extern interrno; #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" #define PERMS 0666 int main(){ char s1[BUFSIZ], s2[BUFSIZ]; intchildpid, readfd, writefd; Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (2) if ((mknod(FIFO1, S_IFIFO | PERMS, 0)<0) && (errno!=EEXIST)) { printf("can't create fifo1: %s", FIFO1); exit(1); } if ((mknod(FIFO2, S_IFIFO | PERMS, 0)<0) && (errno!=EEXIST)) { unlink(FIFO1); printf("can't create fifo2: %s", FIFO2); exit(1); } if ((childpid=fork())<0) { printf("can't fork"); exit(1); } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (3) else if (childpid>0) { /* parent */ if ((writefd=open(FIFO1,1))<0) perror("parent: can't open writefifo"); if ((readfd=open(FIFO2,0))<0) perror("parent: can't open readfifo"); printf("\nParent inputs string and write to FIFO1: "); gets(s1); s1[strlen(s1)]=0; write(writefd,s1,strlen(s1)+1); read(readfd,s2,BUFSIZ); printf("\nFeedback data from FIFO2: %s\n",s2); while (wait((int*)0)!=childpid); /* wait for child finish */ close(readfd); close(writefd); if (unlink(FIFO1)<0) perror("Can't unlink FIFO1"); if (unlink(FIFO2)<0) perror("Can't unlink FIFO2"); exit(0); } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (4) else { /* child */ if ((readfd=open(FIFO1,0))<0) perror("child: can't open readfifo"); if ((writefd=open(FIFO2,1))<0) perror("child: can't open writefifo"); read(readfd,s2,BUFSIZ); printf("\nChild read from FIFO1: %s\n",s2); printf("\nInput string from child to feedback: "); gets(s1); s1[strlen(s1)]=0; write(writefd,s1,strlen(s1)+1); close(readfd); close(writefd); exit(0); } } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình trên Linux • Lậptrình IPC • Dùng pipe • Dùng signal • Dùng semaphore Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Signals • Dựa vào các sựkiện bất đồng bộ. • Kernel cónhiệm vụgửi (deliver) sựkiện đến process • Các process cóthểtựthiết lập các hành vi ứng xử tương ứng với sựkiện nhận được. Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Một số signals thường gặp • SIGKILL • SIGSTOP • SIGHUP • SIGPIPE • SIGINT • SIGQUIT • … Thamkhảothêmdùngcáclệnhsau $ man 7 signal hoặc info signal $ kill -l $ more /usr/include/bits/signum.h Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Các signal Signal Value Action Comment ------------------------------------------------------------------------- SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at tty SIGTTIN 21,21,26 Stop tty input for background process SIGTTOU 22,22,27 Stop tty output for background process Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hành vi của process đối với một sự kiện • Các hành vi có thể thực hiện khi có sự kiện • Thực hiện các hành vi mặc định (do hệ điều hành qui định) • TERMINATED/ABORT - kết thúc • CORE (Dump) - tạo ra core image (memory dump) và thoát • STOP – Trì hoãn thực thi • IGNORE - Bỏ qua signal (ngoại trừ SIGKILL, SIGSTOP) • CATCH -Thực hiện hành vi do người lập trình định nghĩa (signalhandler). Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Các nguồn tạo signal • Từ kernel • Khi xảy ra một số điều kiện vềphần cứng (SIGSEGV, SIGFPE) • Khi xảy ra điều kiện phần mềm (SIGIO) • Từ user • Tổhợpphím: Ctrl+C, Ctrl+Z, Ctrl+\ • Khi user dùnglệnh kill • Từmột process thựchiện system call kill() #include <sys/types.h> #include <signal.h> int kill(pid_tpid, int sig); • Từlờigọi system call alarm()→ tạora SIGALRM Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình với signal #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); int sighold(int sig); int sigrelse(int sig); int sigignore(int sig); int sigpause(int sig); Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình với signal (2) sighandler_t signal(int signum, sighandler_t handler); • Thay đổi hành vi của process đối với signal • Tham số của hàm signal() • signum:làsốhiệu signal màbạn muốn thay đổi hành vi(trừSIGKILL haySIGSTOP) -dạng sốhay symbolic • handler: hành vi mới đối với signal, các giátrịcóthểlà: • SIG_DFL: thiết lập lại hành vi vềmặc định (default) • SIG_IGN: lờ đi (ignore) signal tương ứng • Tham chiếu đến hàm xửlý sựkiện (signal-handler) mới do người dùng tự định nghĩa Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ lập trình với signal Dịch vàthực thi $gcc sigign.c –o sigign $./sigign ^C ^C ^C Bỏ qua signal (sigign.c) #include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> int main() { if (signal(SIGINT, SIG_IGN)==SIG_ERR) { perror("SIGINT\n"); exit(3); } while (1); return 0; } Lưu ý: SIGINT tạo ra khi user nhấn Ctrl+C đểkết thúc process đang thực thi Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ lập trình với signal (2) Định nghĩa hành vi mới đối với tín hiệu SIGINT(sig2.c) #include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> void newhandler(int sig) { printf("\nI received signal %d",sig); } int main() { inti=0; if (signal(SIGINT,newhandler)==SIG_ERR){ perror("\nSIGINT"); exit(3); } while (1); return 0; } Dịch vàthực thi $gcc sig2.c -o sig2 $./sig2 ^C I received signal 2 ^C I received signal 2 ^C I received signal 2 Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lập trình trên Linux • Lập trình IPC • Dùng pipe • Dùng signal • Dùng semaphore Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
SystemV IPC • Gồm: message queue, shared memory, semaphore • Cómột sốthuộc tính chung như • Người tạo, người sởhữu (owner), quyền truy cập (perms) • Cóthểtheodõitrạngtháicác IPC bằnglệnhipcs $ipcs ------Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 65536 root 644 110592 11 dest ------Semaphore Arrays -------- key semid owner perms nsems ------Message Queues -------- key msqid owner perms used-bytes messages Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Semaphore • Đồng bộ các process theo giải thuật của semaphore • Biến semaphore • sốnguyên, truy cập qua các hàm do hệ điều hành cung cấp: P (wait), V (signal) • Đảm bảo loại trừ tương hỗ • Trong UNIX System V, semaphore được dùng theo set –danh sách các semaphore. Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Lệnh IPC trong Linux • Theo dõi trạng thái các IPC (gồm message queue, semaphore, shared memory) • ipcs hoặc ipcs -a • Theo dõi trạng thái các semaphore của hệ thống • ipcs -s • Loại bỏmột semaphore (phải đủquyền hạn) • ipcrm sem semid hoặc ipcrm -s semid Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Các thao tác chủ yếu trên đối tượng IPC • Tác vụ get • tạo các đối tượng IPC. semget() • Tác vụ điều khiển: • lấy hoặc thay đổi thuộc tính của các đối tượng IPC semctl() • Specific operations • thay đổi trạng thái hay nội dung của đối tượng IPC nhằm tương tác với quátrình khác. semop() Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hàm semget() • Tạo semaphore: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> intsemget(key_t key, intnsems, intsemflg); • key: giátrịkey choIPC object, nếu key=IPC_PRIVATE thìsemaphore tạo ra chỉ được sửdụng trong nội bộprocess. • nsems: số lượngsemaphore trong semaphoreset, thông thường chỉcần dùng 1 semaphore. • semflag: IPC_CREAT,IPC_EXCL vàcóthểOR với giátrịấn định quyền truy cập (tương tựquyền hạn trên một file). • Vídụ sset1_id=semget(IPC_PRIVATE,1,IPC_CREAT|IPC_EXCL|0600); sset2_id=semget(12345,1,IPC_CREAT|IPC_EXCL|0666); Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Tạo key cho IPC object • Mỗi IPC object (làmột khối shared memory hoặc một semaphore) được xác định bởi số danh định gọi làkey. • key kiểulàkey_t • Process truy cập một IPC_object thông qua keycủa đối tượng đó. • Cáchtạomột IPC key dùnghàmftok() #include <sys/types.h> #include <sys/ipc.h> keyt_t key; char *path; int id=123; ... key=ftok(path,id); ⇒các process khác nhau chỉcần cung cấp pathvàidgiống nhau làcóthểtạo đúng key truy cập đến cùng một IPC object. Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hàm semop() • Thực hiện các thao tác trên semaphore #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops); • semid: semaphore set ID do hàm semget() trảvề • nsops: số semaphores trong semaphore cần thao tác • sops:làdanh sách gồmnsopscấu trúc sembuf định ra các thao tác cho từng semaphore trong tập semaphore. Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hàm semop() (2) • Cấutrúcsembuf structsembuf { ushortsem_num; /*semaphore thứ #*/ short sem_op; /*operation*/ short sem_flg; /*operation flags*/ } • sem_num:chỉsốcủa semaphoretrongsemaphore set, chỉsốnày bắt đầu từ0 • sem_op: làsốnguyên • >0: tăng giátrịsemaphore • <0: giảmgiátrị semaphore • sem_flg: • IPC_NOWAIT: non-blocking mode • SEM_UNDO: undo operation Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hành vi của hàm semop() • semop<0 • semval ≥abs(semop) semval=semval-abs(semop) • semval ≥abs(semop) & SEM_UNDO semval-=abs(semop) AND semadj+=abs(semop) • semval<abs(semop) block until semval ≥abs(semop) • semval<abs(semop) & IPC_NOWAIT return -1, errno=EAGAIN • semop>0 • semval+=semop • SEM_UNDO • semval+=semop AND semadj-=semop Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hàmsemctl() • Thực hiện các tác vụ thay đổi trạng thái semaphore: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> intsemctl(intsemid,intsemnum,intcmd); intsemctl(intsemid,intsemnum,intcmd, unionsemunarg); union semun{ intval; structsemid_ds *buf; ushort *array; }; Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Hàmsemctl() - thamsốcmd • Các thao tác thông thường • IPC_SET : thiếtlậpquyềntruycập • IPC_STAT: lấythông tin thuộctính • IPC_RMID: xoá semaphore set • Cácthaotáctrêntừng semaphore riênglẻ • GETVAL: lấythông tin thuộctính • SETVAL: thay đổi thuộc tính • GETPID: lấy PID của process vừatruycập semaphore • GETNCNT: lấy sốprocessđang đợisemvaltăng lên • GETZCNT: lấy số process đang đợi semvalvề0 • Cácthaotáctrêntoàn semaphore set • SETALL: thayđổithuộctính • GETALL: lấythông tin thuộctính Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ • Hiện thực 4 hàm cơ bản của semaphore • seminit: tạo binary semaphore • p (wait) • v (signal) • semrel: xoá semaphore • Viết chương trình giải quyết tranh chấp dùng semaphore Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (sema.c) #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> #include <unistd.h> union { intval; structsemid_ds *buf; ushort *array; } carg; intseminit() { inti, semid; if (semid=semget(IPC_PRIVATE,1,0666|IPC_EXCL)==-1) return(-1); carg.val=1; if (semctl(semid,0,SETVAL,carg)==-1) return(-1); return semid; } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (sema.c) (2) void p(int sem){ struct sembuf pbuf; pbuf.sem_num=0; pbuf.sem_op=-1; /* giảm giá trị semaphore */ pbuf.sem_flg=SEM_UNDO; if (semop(sem,&pbuf,1)==-1) { perror("semop"); exit(1); } } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (sema.c) (3) void v(intsem){ structsembufvbuf; vbuf.sem_num=0; vbuf.sem_op=1; vbuf.sem_flg=SEM_UNDO; if (semop(sem,&vbuf,1)==-1) { perror("semop"); exit(1); } } intsemrel(intsemid){ return semctl(semid,0,IPC_RMID,0); } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM
Ví dụ (sema.c) void func(intsem) { while(1) { p(sem); /* enter section */ printf("%d Do something in CS\n",getpid()); sleep(5); v(sem); /* exit section */ printf("\n%d Out of CS\n",getpid()); sleep(1); } } void main() { intsem; sem=seminit(); if (fork()==0) func(sem); else func(sem); } Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM