Linux Programing Linux Programing
Trong Linux
System calls như
System calls System calls
được hiện thực trong close() được hiện thực trong
System call thường là một yêu cầu đến hệ điều System call thường là một yêu cầu đến hệ điều hành để làm một tác vụ phần cứng/chuyên biệt hành để làm một tác vụ phần cứng/chuyên biệt hệ thống hay tác vụ đặc quyền hệ thống . hệ thống hay tác vụ đặc quyền hệ thống . Trong Linux--1.2 có 140 system calls được định 1.2 có 140 system calls được định nghĩa. nghĩa. System calls như close() Linux libc. Linux libc. Việc hiện thực này xoay quanh việc gọi một là Việc hiện thực này xoay quanh việc gọi một là syscall(). syscall(). Các tham số truyền vào syscall() là số hiệu Các tham số truyền vào syscall() là số hiệu system call và được theo sau bởi các tham số system call và được theo sau bởi các tham số cần thiết cần thiết
Số hiệu của system call có tìm thể thấy ở Số hiệu của system call có tìm thể thấy ở < linux/unistd.h > < linux/unistd.h > Trong đó < sys/syscall.h > được cập nhật Trong đó < sys/syscall.h > được cập nhật với libc mới. với libc mới. Nếu có các calls xuất hiện mà chúng chưa Nếu có các calls xuất hiện mà chúng chưa có trong libc, ta có thể gọi syscall(). có trong libc, ta có thể gọi syscall().
System calls System calls
Ví dụ, ta có thể đóng file dùng syscall() như sau
Ví dụ, ta có thể đóng file dùng syscall() như sau
(không khuyến khích):
(không khuyến khích):
#include
System calls System calls
System call Parameters
System call Parameters
Trên kiến trúc i386 , system calls bị giới hạn đến
Trên kiến trúc i386 , system calls bị giới hạn đến
5 tham số không kể đến số hiệu system call bởi
5 tham số không kể đến số hiệu system call bởi
vì việc hạn chế số lượng thanh ghi
vì việc hạn chế số lượng thanh ghi
Nếu bạn dùng Linux trên một kiến trúc khác, ta
Nếu bạn dùng Linux trên một kiến trúc khác, ta
có thể check kiểm tra
Do đó chỉ có những tay chuyên nghiệp mới nên
Do đó chỉ có những tay chuyên nghiệp mới nên
chơi với các syscall macros. Ví dụ đây là một
chơi với các syscall macros. Ví dụ đây là một
hàm close() có dùng syscall macro.
hàm close() có dùng syscall macro.
#include
filedescriptor); filedescriptor);
syscall1 macro mở rộng hàm close(). Do đó ta syscall1 macro mở rộng hàm close(). Do đó ta có close() xuất hiện 2 lần: một trong libc và một có close() xuất hiện 2 lần: một trong libc và một trong chương trình của ta. Giá trị trả về của trong chương trình của ta. Giá trị trả về của syscall() hay syscall macro là 1 nếu system call syscall() hay syscall macro là --1 nếu system call thất bại và 0 hoặc lớn hơn nếu thành công. thất bại và 0 hoặc lớn hơn nếu thành công.
System call Parameters (tt) System call Parameters (tt)
Các system call sau là tồn tại trên BSD và Các system call sau là tồn tại trên BSD và SYS V nhưng không tồn tại trên Linux: SYS V nhưng không tồn tại trên Linux: audit(), auditon(), auditsvc(), fchroot(), audit(), auditon(), auditsvc(), fchroot(), getauid(), getdents(), getmsg(), mincore(), getauid(), getdents(), getmsg(), mincore(), poll(),putmsg(), setaudit(), setauid(). poll(),putmsg(), setaudit(), setauid().
System Call không có trong Linux System Call không có trong Linux
screwdriver, screwdriver, corkscrew, scissors, metal saw, wood saw, can opener, mini screwdriver, nailfile, metal file, wire bender, large blade, small blade, cap lifter, wire stripper, reamer, punch, key ring, tweezers,
multi-purpose hook, chisel, wire cutters, pin, nail cleaner, multipurpose pliers, clef 6 pans 5mm pour connecteurs D- SUB,embout Pozidrive 0, embout Pozidrive 1, embout tournevis, embout Phillips 2, embout Hex (inbus), embout Torx 8, embout Torx 10, embout Torx 15, long ballpoint pen, toothpick
http://www.swiss-knife.com/asp/detail.asp?lan=EN&code=1.7775.T&shop=SK
The “swiss army knife” ioctl The “swiss army knife” ioctl phillips screwdriver, screwdriver,
Giá trị trả về là
ioctl viết tắt cho input/output control và ioctl viết tắt cho input/output control và nó được dùng để thao tác đến các nó được dùng để thao tác đến các character device thông qua filedescriptor. character device thông qua filedescriptor. Format của ioctl là Format của ioctl là ioctl(unsigned int fd, unsigned int request, ioctl(unsigned int fd, unsigned int request, unsigned long argument). . unsigned long argument) 1 nếu có lỗi và =0 nếu Giá trị trả về là --1 nếu có lỗi và =0 nếu request thành công (tương tự như các system request thành công (tương tự như các system call khác). call khác).
The “swiss army knife” ioctl The “swiss army knife” ioctl
Kernel phân biệt các file đặc biệt và file thông Kernel phân biệt các file đặc biệt và file thông thường . File đặc biệt là những file nằm trong thường . File đặc biệt là những file nằm trong /dev và /proc. Chúng khác với các file thông /dev và /proc. Chúng khác với các file thông thường là chúng dấu các giao diện với driver thường là chúng dấu các giao diện với driver và không phải là một file thật sự chứa dữ liệu và không phải là một file thật sự chứa dữ liệu text hay binary. Đây là triết lý của UNIX và text hay binary. Đây là triết lý của UNIX và cho phép dùng các thao tác read/write thông cho phép dùng các thao tác read/write thông thường trên tất cả các file. thường trên tất cả các file. Bạn sẽ cần dùng ioctl nhiều khi thao tác với Bạn sẽ cần dùng ioctl nhiều khi thao tác với các file đặc biệt hơn là với các file thường. các file đặc biệt hơn là với các file thường. Nhưng bạn cũng có thể dùng ioctl trên các file Nhưng bạn cũng có thể dùng ioctl trên các file thường ! thường !
ioctl (tt) ioctl (tt)
rocess CCommunications IPC ommunications--IPC
pháp cho nhiều tiến trình giao tiếp với nhau. Có nhiều phương pháp IPC cho Linux C programmers áp dụng: Half-duplex UNIX pipes FIFOs (named pipes) SYSV style message queues SYSV style semaphore sets SYSV style shared memory segments Networking sockets (Berkeley style, không đề cập ) Full-duplex pipes (STREAMS pipes,, không đề cập ) Các phương pháp này, khi được dùng một cách hiệu quả, sẽ mang lại một framework vững chắc cho client/server development trên bất kỳ một hệ UNIX nào (bao gồm cả Linux).
IInternterPProcess Cơ chế IPC trong Linux cung cấp một phương
Khái ni
duplex UNIX Pipes HalfHalf--duplex UNIX Pipes
ệm căn bản: pipe pipe là một phương là một phương
của một standard output của một của standard input của
Khái niệm căn bản: của việc kết nối standard output pháppháp của việc kết nối tiến trình process vào một standard input tiến trình process vào một tiến trình khác. Pipes là một công cụ IPC cổ tiến trình khác. Pipes là một công cụ IPC cổ nhất, nó có từ giai đoạn phôi thai nhất của HĐH nhất, nó có từ giai đoạn phôi thai nhất của HĐH UNIX. Chúng cung cấp một của việc giao tiêế 1 UNIX. Chúng cung cấp một của việc giao tiêế 1 duplex) giữa các process. chiều (thuật ngữ half--duplex) giữa các process. chiều (thuật ngữ half Tính năng này được dùng rộng rãi, thậm chí Tính năng này được dùng rộng rãi, thậm chí trong shell của Unix trong shell của Unix
ls | sort | lp
duplex UNIX Pipes HalfHalf--duplex UNIX Pipes
Tạo pipes trong C Tạo pipes trong C Để tạo một pipe đơn giản trong C, ta cần Để tạo một pipe đơn giản trong C, ta cần dùng pipe() system call. Nó cần 1 tham số, đó dùng pipe() system call. Nó cần 1 tham số, đó là một array có 2 số integer và nếu thành là một array có 2 số integer và nếu thành công, array sẽ chứa 2 file descriptors mới để công, array sẽ chứa 2 file descriptors mới để được dùng cho pipeline. Sau khi tạo pipe, được dùng cho pipeline. Sau khi tạo pipe, process thường sinh ra 1 tiến trình mới (lưu ý process thường sinh ra 1 tiến trình mới (lưu ý rằng, tiến trình con thừa kế mô tả file đã mở). rằng, tiến trình con thừa kế mô tả file đã mở).
pipes trong C TTạo ạo pipes trong C
SYSTEM CALL: pipe(); SYSTEM CALL: pipe(); PROTOTYPE: int pipe( int fd[2] ); PROTOTYPE: int pipe( int fd[2] ); RETURNS: 0 on success RETURNS: 0 on success 1 on error: errno = EMFILE (no free --1 on error: errno = EMFILE (no free descriptors) descriptors) EMFILE (system file table is full) EMFILE (system file table is full) EFAULT (fd array is not valid) EFAULT (fd array is not valid) NOTES: fd[0] is set up for reading, fd[1] is set NOTES: fd[0] is set up for reading, fd[1] is set up for writing up for writing
pipes trong C TTạo ạo pipes trong C
Giá trị integer đầu tiên trong array được thiết Giá trị integer đầu tiên trong array được thiết lập và mở ra cho việc đọc, trong khi số integer lập và mở ra cho việc đọc, trong khi số integer thứ hai được thiết lập và mở ra cho việc ghi. thứ hai được thiết lập và mở ra cho việc ghi. Nói một cách hình tượng, output của fd1 trở Nói một cách hình tượng, output của fd1 trở thành input cho fd0. Một lần nữa, tất cả data di thành input cho fd0. Một lần nữa, tất cả data di chuyển thông qua pipe đi vào kernel. chuyển thông qua pipe đi vào kernel.
#include
#include
#include
#include
#include
#include
main()
main()
{{
int fd[2];
int fd[2];
pipe(fd);
pipe(fd);
……
}}
/* Send "string" through the output /* Send "string" through the output
side of pipe */ side of pipe */
write(fd[1], string, strlen(string)); write(fd[1], string, strlen(string)); exit(0); exit(0); }} else { else { /* Parent process closes up output /* Parent process closes up output
side of pipe */ side of pipe */
close(fd[1]); close(fd[1]); /* Read in a string from the pipe */ /* Read in a string from the pipe */ nbytes = read(fd[0], readbuffer, nbytes = read(fd[0], readbuffer,
sizeof(readbuffer)); sizeof(readbuffer));
printf("Received string: %s", printf("Received string: %s",
readbuffer); } readbuffer); }
/***MODULE: pipe.c***/
/***MODULE: pipe.c***/
#include
return(0); return(0); }}
of pipe */ of pipe */ close(fd[0]); close(fd[0]);
Ví dụVí dụ
Với half
Các lưu ý trong half-duplex pipes pipes hai chiều có thể được tạo ra bằng cách mở pipes hai chiều có thể được tạo ra bằng cách mở hai pipes, và được gán lại file descriptors một hai pipes, và được gán lại file descriptors một cách thích hợp trong tiến trình con . cách thích hợp trong tiến trình con . Lời gọi pipe() phải được thực hiện trước khi gọi Lời gọi pipe() phải được thực hiện trước khi gọi fork(), nếu không thì descriptors sẽ không được fork(), nếu không thì descriptors sẽ không được thừa kế bởi tiến trình con ! thừa kế bởi tiến trình con ! duplex pipes, bất kỳ tiến trình kết nối Với half--duplex pipes, bất kỳ tiến trình kết nối nào phải được chia sẽ cùng một tổ tiên. Khi pipe nào phải được chia sẽ cùng một tổ tiên. Khi pipe cư trú bên trong sự kềm hãm của kernel, bất cứ cư trú bên trong sự kềm hãm của kernel, bất cứ một tiến trình nào mà không cùng tổ tiên của một tiến trình nào mà không cùng tổ tiên của pipe thì không có cách nào tìm đến được nó. pipe thì không có cách nào tìm đến được nó.
Khái niệm: named pipe hoạt động giống Khái niệm: named pipe hoạt động giống như pipe thông thường, nhưng có một số như pipe thông thường, nhưng có một số khác biệt đáng chú ý: khác biệt đáng chú ý: Named pipes tồn tại như là một device special Named pipes tồn tại như là một device special file trong file system. file trong file system. Các tiến trình khác tổ tiên có thể chia sẽ data Các tiến trình khác tổ tiên có thể chia sẽ data thông qua một named pipe. thông qua một named pipe. Khi tất cả I/O được hoàn tất bởi các tiến trình Khi tất cả I/O được hoàn tất bởi các tiến trình chia sẻ, named pipe còn lưu lại trong file chia sẻ, named pipe còn lưu lại trong file system để dùng sau. system để dùng sau.
Named Pipes (FIFOs) Named Pipes (FIFOs)
Có nhiều cách để tạo một named pipe. Hai Có nhiều cách để tạo một named pipe. Hai cách đầu tiên là tạo trực tiếp từ shell. cách đầu tiên là tạo trực tiếp từ shell. mknod MYFIFO p mknod MYFIFO p mkfifo a=rw MYFIFO mkfifo a=rw MYFIFO FIFO files có thể được xác định trong FIFO files có thể được xác định trong physical file system bởi ký tự “p” ở đầu tiên: physical file system bởi ký tự “p” ở đầu tiên: [root@pascal root]# ls l MYFIFO [root@pascal root]# ls --l MYFIFO 1 root root 0 Aug 23 23:35 MYFIFO prwprw--rr----rr---- 1 root root 0 Aug 23 23:35 MYFIFO [root@pascal root]# [root@pascal root]#
Tạo FIFO Tạo FIFO
Để tạo FIFO trong C, ta có thể dùng Để tạo FIFO trong C, ta có thể dùng mknod() system call: mknod() system call: LIBRARY FUNCTION: mknod(); LIBRARY FUNCTION: mknod(); PROTOTYPE: int mknod( char *pathname, PROTOTYPE: int mknod( char *pathname,
mode_t mode, dev_t dev); mode_t mode, dev_t dev); RETURNS: 0 on success, RETURNS: 0 on success, 1 on error:errno=EFAULT (pathname invalid) --1 on error:errno=EFAULT (pathname invalid)
EACCES (permission denied) EACCES (permission denied) ENAMETOOLONG (pathname ENAMETOOLONG (pathname too long) too long) ENOENT (invalid pathname) ENOENT (invalid pathname) ENOTDIR (invalid pathname) ENOTDIR (invalid pathname)
NOTES: Tạo filesystem node (file, device file, or FIFO) NOTES: Tạo filesystem node (file, device file, or FIFO)
Tạo FIFO trong C Tạo FIFO trong C
Một mẹo nhỏ dùng umask() system call để tạm Một mẹo nhỏ dùng umask() system call để tạm thời vô hiệu giá trị umask : thời vô hiệu giá trị umask : umask(0); umask(0); mknod("/tmp/MYFIFO", S_IFIFO|0666, 0); mknod("/tmp/MYFIFO", S_IFIFO|0666, 0);
mknodmknod Muốn biến rõ hơn về mknod thì hãy dùng lệnh Muốn biến rõ hơn về mknod thì hãy dùng lệnh ,nhưng xét ví dụ đơn giản sau trong C: manman,nhưng xét ví dụ đơn giản sau trong C: mknod("/tmp/MYFIFO", S_IFIFO|0666, 0); mknod("/tmp/MYFIFO", S_IFIFO|0666, 0); Ở đây, file “/tmp/MYFIFO” được tạo ra như là Ở đây, file “/tmp/MYFIFO” được tạo ra như là một FIFO file. Quyền của file là “0666”, mặc dù một FIFO file. Quyền của file là “0666”, mặc dù chúng bị ảnh hưởng bởi umask setting như sau: chúng bị ảnh hưởng bởi umask setting như sau: final_umask = requested_permissions & final_umask = requested_permissions & ˜original_umask ˜original_umask
Các thao tác I/O trên FIFO là cần thiết như đối Các thao tác I/O trên FIFO là cần thiết như đối với các pipe thông thường, với một ngoại lệ với các pipe thông thường, với một ngoại lệ chính yếu. Một “open” system call hay hàm thư chính yếu. Một “open” system call hay hàm thư viện cần phải được dùng để physically open up viện cần phải được dùng để physically open up duplex pipes, điều một kênh tới pipe. Với half--duplex pipes, điều một kênh tới pipe. Với half này là không cần thiết, bởi vì khi đó pipe nằm này là không cần thiết, bởi vì khi đó pipe nằm trong kernel và không phải trên physical trong kernel và không phải trên physical filesystem. Trong ví dụ, chúng ta sẽ xem như filesystem. Trong ví dụ, chúng ta sẽ xem như pipe là một stream, mở nó ra với fopen() và pipe là một stream, mở nó ra với fopen() và đóng với fclose(). đóng với fclose().
Thao tác FIFO Thao tác FIFO
#include
fifoserver.c fifoserver.c
while(1) while(1) {{ fp = fopen(FIFO_FILE, "r"); fp = fopen(FIFO_FILE, "r"); fgets(readbuf, 80, fp); fgets(readbuf, 80, fp); n", readbuf); printf("Received string: %s\\n", readbuf); printf("Received string: %s fclose(fp); fclose(fp); }} return(0); return(0); }}
fifoserver.c (tt) fifoserver.c (tt)
#include
fifoclient.c fifoclient.c
if((fp = fopen(FIFO_FILE, "w")) == NULL) { if((fp = fopen(FIFO_FILE, "w")) == NULL) { perror("fopen"); perror("fopen"); exit(1); exit(1); }} fputs(argv[1], fp); fputs(argv[1], fp); fclose(fp); fclose(fp); return(0); return(0); }}
fifoclient.c (tt) fifoclient.c (tt)
Thông thường, blocking xảy ra trên FIFO. Thông thường, blocking xảy ra trên FIFO. Nói cách khác nếu FIFO được mở để đọc, Nói cách khác nếu FIFO được mở để đọc, process sẽ ”block” cho đến khi một vài process sẽ ”block” cho đến khi một vài process khác mở nó ra để viết vào. Nếu process khác mở nó ra để viết vào. Nếu hành vi này là không mong muốn thì một hành vi này là không mong muốn thì một cờ O_NONBLOCK có thể được dùng trong cờ O_NONBLOCK có thể được dùng trong open() call để disable default blocking open() call để disable default blocking action. action.
Blocking Actions on a FIFO Blocking Actions on a FIFO
Một chú ý cuối cùng, pipes phải có một Một chú ý cuối cùng, pipes phải có một reader và một writer. Nếu một process cố reader và một writer. Nếu một process cố gắng ghi vào một pipe mà không có gắng ghi vào một pipe mà không có reader, nó sẽ được gửi đến 1 tín hiệu reader, nó sẽ được gửi đến 1 tín hiệu SIGPIPE từ kernel. Điều này là bắt buộc SIGPIPE từ kernel. Điều này là bắt buộc khi có nhiều hơn hai tiến trình thao tác với khi có nhiều hơn hai tiến trình thao tác với một pipeline. một pipeline.
Infamous SIGPIPE Signal Infamous SIGPIPE Signal
Introduction Introduction Socket Socket Client.c Client.c Server.c Server.c
TCP/IP in Linux TCP/IP in Linux
Introduction Introduction
Socket Socket
len = sizeof(address); len = sizeof(address);
result = connect(sockfd, result = connect(sockfd,
(struct sockaddr (struct sockaddr
*)&address, len); *)&address, len); if(result == --1) {1) { if(result ==
#include
perror("oops: client1 problem"); perror("oops: client1 problem"); exit(1); exit(1);
}} write(sockfd, &ch, 1); write(sockfd, &ch, 1); read(sockfd, &ch, 1); read(sockfd, &ch, 1); printf("char from server = %c printf("char from server = %c\\n", n", ch); ch); close(sockfd); close(sockfd); exit(0); exit(0);
}}
int sockfd; int len; struct sockaddr_in address; int result; char ch = 'A'; sockfd = socket(AF_INET, SOCK_STREAM, 0); address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr("127.0.0.1"); address.sin_port = 9734;
Client.c Client.c
bind(server_sockfd,
(struct sockaddr
#include
*)&server_address, server_len); listen(server_sockfd, 5); while(1) { char ch; printf("server waiting\n"); client_sockfd = accept(server_sockfd, (struct sockaddr
int server_sockfd, client_sockfd; int server_len, client_len; struct sockaddr_in server_address; struct sockaddr_in client_address; server_sockfd = socket(AF_INET, SOCK_STREAM, 0); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr =
inet_addr("127.0.0.1"); server_address.sin_port = 9734; server_len = sizeof(server_address);
*)&client_address, &client_len); read(client_sockfd, &ch, 1); ch++; write(client_sockfd, &ch, 1); close(client_sockfd);
}
}
Server.c Server.c