Bài giảng Thực hành UNIX/Linux - Phần 3
Lập trình trên Linux (2)
Cơ bản về lập trình POSIX pthread
Lập trình IPC dùng signal, pipe, shared memory
Giải quyết tranh chấp bằng SVR4 semaphore
Giải quyết tranh chấp trên POSIX thread (tự đọc
thêm)
Tài liệu tham khảo
1. John Shapley Gray: Interprocess Communications in Linux : The
Nooks & Crannies, Prentice Hall PTR (2003)
Cơ bản về lập trình POSIX pthread
Lập trình IPC dùng signal, pipe, shared memory
Giải quyết tranh chấp bằng SVR4 semaphore
Giải quyết tranh chấp trên POSIX thread (tự đọc
thêm)
Tài liệu tham khảo
1. John Shapley Gray: Interprocess Communications in Linux : The
Nooks & Crannies, Prentice Hall PTR (2003)
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Thực hành UNIX/Linux - Phần 3", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.
File đính kèm:
- bai_giang_thuc_hanh_unixlinux_phan_3.pdf
Nội dung text: Bài giảng Thực hành UNIX/Linux - Phần 3
- VVíí ddụụ vvềề llậậpp trtrììnhnh POSIXPOSIX threadthread (t.t)(t.t) Chú ý quan trọng khi lập trình lập trình POSIX z Phải có chỉ thị #include trong chương trình z Khi biên dịch phải có chỉ định liên kết với thư viện POSIX pthread bằng tùy chọn -lpthread của GCC (một số version GCC mới có thể đã có thiết lập mặc định này nên dù user không chỉ định vẫn biên dịch được) $gcc thr1.c -o thr1 /tmp/cc1PSqgE.o: In function `main': /tmp/cc1PSqgE.o(.text+0x86): undefined reference to `pthread_create' collect2: ld returned 1 exit status z Có thể dùng lệnh ldd để kiểm tra xem file thực thi tạo ra là thr1 phụ thuộc thư viện nào $ldd thr1 libpthread.so.0 => /lib/i686/libpthread.so.0 (0x40028000) libc.so.6 => /lib/i686/libc.so.6 (0x42000000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.11
- CCáácc hhààmm llậậpp trtrììnhnh khkháácc PTHREAD_JOIN #include int pthread_join(pthread_t th, void thread_return); Hàm pthread_join() khiến cho thread gọi hàm phải tạm ngưng thực thi, đợi cho đến khi thread có chỉ số thread ID th kết thúc. Tham số thread_return chứa kết quả trả về (chính là giá trị của lệnh return hoặc hàm pthread_exit( )) Nếu thread kia đã kết thúc trước lời gọi hàm -> lỗi (nonzero) Nếu có nhiều thread cùng gọi join đến một thread thì cuối cùng chỉ có một thread (?) được báo kết quả chính xác !!! Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.12
- VVíí ddụụ pthread_join()pthread_join() File thr2.c 1. #include 2. #include 3. void* func(void* arg) 4. { 5. int i; 6. for (i = 0; i < 2; i++) { 7. printf ("This is thread %d\n", *((int*)arg)); 8. sleep (1); 9. } 10. } 11. int main (int argc, char argv) { 12. int i; 13. pthread_t tid[3]; 14. for (i = 0; i < 3; i++){ 15. pthread_create (&tid[i], NULL, func, (void*)&tid[i]); 16. pthread_join (tid[i], NULL); 17. } 18. return 0; 19. } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.13
- TruyTruyềềnn ddữữ liliệệuu chocho threadthread #include #include struct char_print_parms { char character; int count; }; void* char_print (void* args) { struct char_print_parms* p = (struct char_print_parms*) args; int i; for (i = 0; i count; ++i) printf ("%c", p->character); return NULL; } int main () { pthread_t tid; struct char_print_parms th_args; th_args.character = 'X'; th_args.count = 300; pthread_create (&tid, NULL, &char_print, &th_args); pthread_join (tid, NULL); return 0; } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.14
- GiGiớớii thithiệệuu vvềề IPCIPC Mục tiêu của IPC z Cho phép phối hợp hoạt động giữa các quá trình trong hệ thống z Giải quyết đụng độ trên vùng tranh chấp z Truyền thông điệp từ quá trình này đến các quá trình khác z Chia sẻ thông tin giữa các quá trình với nhau Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.15
- GiaoGiao titiếếpp vvàà đđồồngng bbộộ Communication Synchronization z Truyền dữ liệu z Giải quyết tranh chấp z Chia sẻ thông tin z Đảm bảo thứ tự xử lý z Các cơ chế: z Các cơ chế: Lock files Signal Semaphores Pipes Mutex (pthread) Message queues Shared memory Sockets RPC/RMI Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.16
- SignalsSignals 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. Signals (events) Process Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.17
- MMộộtt ssốố signalssignals ththưườờngng ggặặpp SIGKILL SIGSTOP SIGHUP SIGPIPE SIGINT SIGQUIT Tham khảo thêm dùng các lệnh sau $ man signal hoặc info signal $ kill -l $ more /usr/include/bits/signum.h Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.18
- HHàànhnh vivi ccủủaa processprocess đđốốii vvớớii mmộộtt ssựự kikiệệnn Các hành vi có thể thực hiện khi có sự kiện z 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 z IGNORE - Bỏ qua signal (ngoại trừ SIGKILL, SIGSTOP) z CATCH - Thực hiện hành vi do người lập trình định nghĩa (signal handler). Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.19
- CCáácc ngunguồồnn ttạạoo signalsignal Từ kernel z Khi xảy ra một số điều kiện về phần cứng (SIGSEGV, SIGFPE) z Khi xảy ra điều kiện phần mềm (SIGIO) Từ user z Tổ hợp phím: Ctrl + C, Ctrl+Z, Ctrl + \ z Khi user dùng lệnh kill Từ một process thực hiện system call kill(): #include #include int kill(pid_t pid, int sig); Từ lời gọi system call alarm() → tạo ra SIGALARM Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.20
- LLậậpp trtrììnhnh vvớớii signalsignal #include 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.21
- LLậậpp trtrììnhnh vvớớii signalsignal (t.t)(t.t) 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() z signum:làsố hiệu signal mà bạn muốn thay đổi hành vi (trừ SIGKILL hay SIGSTOP) - dạng số hay symbolic z 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.22
- VVíí ddụụ llậậpp trtrììnhnh vvớớii signalsignal Bỏ qua signal (sig1.c): Dịch và thực thi: #include $ gcc sig1.c –o sig1 #include $./sig1 #include #include ^C int main() { ^C int i=0; ^C if (signal(SIGINT, SIG_IGN) == SIG_ERR) { perror("\nSIGINT"); 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.23
- VVíí ddụụ llậậpp trtrììnhnh vvớớii signalsignal (t.t)(t.t) Định nghĩa hành vi mới đối với tín hiệu SIGINT (sig2.c): #include Dịch và thực thi #include #include $ gcc sig2.c -o sig2 #include $ ./sig2 void newhandler(int sig) { ^C printf("\nI received signal %d", sig); I received signal 2 } int main() { ^C int i=0; I received signal 2 if (signal(SIGINT, newhandler) == SIG_ERR) { ^C perror("\nSIGINT"); exit(3); I received signal 2 } while (1); return 0; } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.24
- GiaoGiao titiếếpp thôngthông quaqua PIPEPIPE Là kênh truyền dữ liệu giữa các process với nhau theo dạng FIFO Writer Reader Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.25
- CCáácc ttáácc vvụụ trêntrên pipepipe Write: #include ssize_t write (int filedes, const void *buf, size_t count) Read: #include ssize_t read (int filedes, const void *buf, size_t count) Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.26
- HaiHai loloạạii pipepipe z Unnamed pipe có ý nghĩa cục bộ chỉ dành cho các process có quan hệ bố con với nhau z 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.27
- UnnamedUnnamed pipepipe Tạo unnamed pipe: #include int pipe( int filedes[2]); Kết quả z 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] z Thất bại: hàm pipe() trả về -1, mã lỗi trong biến ngoại errno Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.28
- UnnamedUnnamed pipepipe filedes1[1] Æ filedes1[0] Process 1 Process 2 filedes2[0] Æ filedes2[1] Duplex z 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 z 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.29
- VVíí ddụụ vvềề unnamedunnamed pipepipe #include #include Dịch, thực thi #include #include $ gcc pipe.c -o pipe int main() { $ ./pipe int fp[2]; char s1[BUFSIZ]; char s2[BUFSIZ]; Input: I Love Penguin pipe(fp); if (fork()==0) { From pipe> I Love Penguin printf("\nInput:"); $ fgets(s1, BUFSIZ, stdin); s1[strlen(s1)]=0; close(fp[0]); write(fp[1], s1, strlen(s1)+1); } else { close(fp[1]); read(fp[0], s2, BUFSIZ); printf("\nFrom pipe > %s\n", s2); } return 0; } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.30
- DDùùngng pipepipe đđểể ttááii đđịịnhnh hhưướớngng 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) z 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: z dup() z dup2() Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.31
- dup()dup() #include int dup(int filedes); Chức năng: tạo bản sao của descriptor filedes. File descriptor nhỏ nhất còn chưa sử dụng sẽ là bản sao của filedes. Kết quả của hàm dup() z Thành công: file descriptor bản sao z Lỗi: -1(mã lỗi trong biến ngoại errno) File descriptor mới có những thuộc tính chung với file ban đầu z Cùng chỉ đến một file (hay pipe) đang mở z Chia sẻ cùng một file pointer z Có cùng một chế độ truy cập (read, write hoặc read/write) 0 stdin 0 stdin dup(1); 1 stdout 1 stdout ror 2 stderror 2 stder 3 available 3 file pointed by available 4 4 file descriptor 1 Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.32
- dup2()dup2() #include int dup2(int filedes, int filedes2); Tương tự dup(), nhưng dup2() cho phép chỉ định file descriptor đích z Descriptor filedes2 trỏ đến cùng một file như filedes stdin 0 stdin 0 dup2(1, 4); stdout 1 stdout 1 stderror 2 stderror 2 3 3 file pointed by 4 4 file descriptor 0 Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.33
- NamedNamed pipepipe Tương tự như unnamed pipe Một số tính năng cần chú ý: z Được ghi nhận trên file system (directory entry, file permission) z Có thể dùng với các process có quan hệ bố con z Cóthể tạo ra từ dấu nhắc lệnh shell (bằng lệnh mknod) Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.34
- TTạạoo unnamedunnamed pipepipe mknodmknod()() System call: #include #include int mknod(const char *path, mode_t mode, dev_t dev); Trong đó z path: đường dẫn đến pipe (trên file system) z mode: quyền truy cập trên file = S_IFIFO kết hợp với trị khác z dev: dùng giá trị 0 C/C++ library call #include #include int mkfifo (const char *pathname, mode_t mode); Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.35
- VVíí ddụụ #include #include Dịch &thực thi #include $gcc fifo.c –o fifo #include $./fifo Parent inputs string and write to FIFO1:Test1 #include extern int errno; Child read from FIFO1:Test1 Input string from child to feedback:Test2 #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" Feedback data from FIFO2:Test2 #define PERMS 0666 int main(){ char s1[BUFSIZ], s2[BUFSIZ]; int childpid, readfd, writefd; Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.36
- VVíí ddụụ if ( (mknod(FIFO1, S_IFIFO | PERMS, 0) < 0) && (errno !=EEXIST)) { printf("can't create fifo 1: %s", FIFO1); exit(1); } if ( (mknod(FIFO2, S_IFIFO | PERMS, 0) < 0) && (errno !=EEXIST)) { unlink(FIFO1); printf("can't create fifo 2: %s", FIFO2); exit(1); } if ( (childpid = fork()) < 0) { printf("can't fork"); exit(1); } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.37
- VVíí ddụụ else if (childpid > 0) { /* parent */ if ((writefd = open(FIFO1, 1)) < 0) perror("parent: can't open write fifo"); if ((readfd = open(FIFO2, 0)) < 0)perror("parent: can't open read fifo"); 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.38
- VVíí ddụụ else { /* child */ if ( (readfd = open(FIFO1, 0)) < 0) perror("child: can't open read fifo"); if ( (writefd = open(FIFO2, 1)) < 0) perror("child: can't open write fifo"); 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.39
- SystemVSystemV IPCIPC Gồm: message queue, shared memory, semaphore Có một số thuộc tính chung như z Người tạo, người sở hữu (owner), quyền truy cập (perms) Có thể theo dõi trạng thái các IPC bằng lệnh ipcs $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 status Message Queues key msqid owner perms used-bytes messages Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.40
- LLệệnhnh IPCIPC trongtrong LinuxLinux Theo dõi trạng thái các IPC (gồm message queue, semaphore, shared memory) z ipcs hoặc ipcs -a Theo dõi trạng thái các semaphore của hệ thống z ipcs -s Theo dõi trạng thái các vùng nhớ chia sẻ của hệ thống z ipcs -m Loại bỏ một semaphore (phải đủ quyền hạn) z ipcrm sem sem_id hoặc ipcs -s sem_id Loại bỏ một vùng nhớ chia sẻ (phải đủ quyền hạn) z ipcrm shm shm_id hoặc ipcs -shm shm_id Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.41
- CCáácc thaothao ttáácc chchủủ yyếếuu trêntrên đđốốii ttưượợngng IPCIPC Tác vụ get z tạo các đối tượng IPC. Ví dụ semget(), shmget() Tác vụ điều khiển: z lấy hoặc thay đổi thuộc tính của các đối tượng IPC semctl(), shmctl() Specific operations z 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. Ví dụ semop() shmat(), shmdt() Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.42
- SemaphoreSemaphore Đồng bộ các process theo giải thuật của semaphore Biến semaphore z 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.43
- HHààmm semgetsemget()() Tạo semaphore: key: giá trị key cho IPC object, nếu key = IPC_PRIVATE thì semaphore tạo ra chỉ được sử dụng trong nội bộ process. nsems: số lượng semaphore trong semaphore set, 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ụ là: sset1_id = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | 0600); sset2_id = semget(12345, 1, IPC_CREAT | IPC_EXCL | 0666); Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.44
- TTạạoo keykey chocho IPCIPC objectobject 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. z Key kiểu là key_t Process truy cập một IPC_object thông qua key của đối tượng đó. Cách tạo một IPC key dùng hàm ftok() ⇒ các process khác nhau chỉ cần cung cấp path và id giống nhau là có thể tạo đúng key truy cập đến cùng một IPC object. Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.45
- HHààmm semopsemop()() Thực hiện các thao tác trên semaphore 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ồm nsops cấu trúc sembuf định ra các thao tác cho từng semaphore trong tập semaphore. Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.46
- HHààmm semopsemop()() Cấu trúc sembuf struct sembuf { ushort sem_num; /*semaphore #*/ short sem_op; /*operation*/ short sem_flg; /*operation flags*/ } sem_num :chỉ số của semaphore trong semaphore set, chỉ số này bắt đầu từ 0 sem_op :làsố nguyên, >0: tăng giá trị semaphore <0: giảm giá trị semaphore sem_flg : IPC_NOWAIT: non-blocking mode SEM_UNDO: undo operation Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.47
- HHàànhnh vivi ccủủaa hhààmm semop()semop() 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 Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.48
- semctlsemctl()() Thực hiện các tác vụ thay đổi trạng thái semaphore: #include #include #include int semctl(int semid, int semnum, int cmd); int semctl(int semid, int semnum, int cmd, union semun arg); union semun { int val; struct semid_ds *buf; ushort *array; }; Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.49
- semctl()semctl() thamtham ssốố cmdcmd Các thao tác thông thường: IPC_SET : thiết lập quyền truy cập IPC_STAT: lấy thông tin thuộc tính IPC_RMID: xoá semaphore set Các thao tác trên từng semaphore riêng lẻ: GETVAL: lấy thông tin thuộc tính SETVAL: thay đổi thuộc tính GETPID: lấyPID của process vừa truy cập semaphore GETNCNT: lấy số process đang đợi semval tăng lên GETZCNT: lấy số process đang đợi semval về 0 Các thao tác trên toàn semaphore set: SETALL: thay đổi thuộc tính GETALL: lấy thông tin thuộc tính Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.50
- VVíí ddụụ Hiện thực 4 hàm cơ bản của semaphore z seminit: tạo binary semaphore z P (wait) z V (signal) z semrel: xoá semaphore Viết chương trình giải quyết tranh chấp dùng semaphore Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.51
- VVíí ddụụ ((semasema.h).h) #include #include #include #include #include union { int val; struct semid_ds *buf; ushort *array; } carg; int seminit(int val) { int i, semid; semid=semget(IPC_PRIVATE, 1, 0666|IPC_CREAT); if (semid==-1) return(-1); carg.val=1; if (semctl(semid, val, SETVAL, carg)==-1) return(-1); return (semid); } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.52
- VVíí ddụụ ((semasema.h).h) 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 Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.53
- VVíí ddụụ ((semasema.h).h) void v(int sem) { struct sembuf vbuf; vbuf.sem_num=0; vbuf.sem_op=1; vbuf.sem_flg=SEM_UNDO; if (semop(sem, &vbuf, 1)== -1) { perror("semop"); exit(1); } } int semrel (int semid) { return (semctl(semid, 0,IPC_RMID, 0)); } Khoa Công nghệ Thông tin - Đại học Bách Khoa Tp. HCM 3.54