1 / 50

Thực hành Unix, Linux (3) Ngô Thanh Nguyên Bộ môn Hệ thống và Mạng máy tính

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.

asis
Download Presentation

Thực hành Unix, Linux (3) Ngô Thanh Nguyên Bộ môn Hệ thống và Mạng máy tính

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  2. 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

  3. 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

  4. 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

  5. Giao tiếp và đồng bộ Khoa KH&KTMT - Đại học Bách Khoa Tp. HCM

  6. 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

  7. 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

  8. 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

  9. 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

  10. 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

  11. 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

  12. 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

  13. 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

  14. 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

  15. 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

  16. 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

  17. 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

  18. 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

  19. 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

  20. 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

  21. 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

  22. 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

  23. 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

  24. 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

  25. 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

  26. 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

  27. 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

  28. 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

  29. 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

  30. 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

  31. 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

  32. 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

  33. 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

  34. 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

  35. 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

  36. 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

  37. 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

  38. 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

  39. 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

  40. 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

  41. 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

  42. 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

  43. 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

  44. 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

  45. 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

  46. 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

  47. 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

  48. 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

  49. 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

  50. 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

More Related