System call Parameters 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 vì việc hạn chế số lượng thanh ghi có thể check kiểm tra để tìm
Trang 1Linux Programing
Trang 3 Nếu có các calls xuất hiện mà chúng chưa
có trong libc, ta có thể gọi syscall().
Trang 4System calls
Ví dụ, ta có thể đóng file dùng syscall() như sau (không khuyến khích):
#include <syscall.h>
extern int syscall(int, );
int my_close(int filedescriptor)
{
return syscall(SYS_close, filedescriptor);
}
Trang 5System call Parameters
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
vì việc hạn chế số lượng thanh ghi
có thể check kiểm tra <asm/unistd.h> để tìm các syscall macros để xem phần cứng của bạn có thể
hỗ trợ đến bao nhiêu tham số
cho syscall(), nhưng nó không được khuyến khích khi đã có những macro được phát triển thành các hàm đầy đủ tồn tại trong thư viện lập trình
Trang 6System call Parameters (tt)
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
hàm close() có dùng syscall macro
#include <linux/unistd.h>
_syscall1(int, close, int,
filedescriptor);
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 trong chương trình của ta Giá trị trả về của
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
Trang 7System Call không có trong Linux
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:
audit(), auditon(), auditsvc(), fchroot(),
getauid(), getdents(), getmsg(), mincore(),
poll(),putmsg(), setaudit(), setauid()
Trang 8The “swiss army knife” ioctl
screwdriver, screwdriver, corkscrew, scissors, metal saw, wood saw, can
metal file, wire bender, large blade, small blade, cap lifter, wire stripper,
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
http://www.swiss-knife.com/asp/detail.asp?lan=EN&code=1.7775.T&shop=SK
Trang 9The “swiss army knife” ioctl
ioctl viết tắt cho input/output control và
nó được dùng để thao tác đến các
character device thông qua filedescriptor
Format của ioctl là
ioctl(unsigned int fd, unsigned int request,
unsigned long argument)
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 call khác)
Trang 10ioctl (tt)
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 /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 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à cho phép dùng các thao tác read/write thông 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 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 thường !
Trang 11InterProcess Communications-IPC
Cơ chế IPC trong Linux cung cấp một phương 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).
Trang 12Half-duplex UNIX Pipes
Khái niệm căn bản: pipe là một phương
pháp của việc kết nối standard output của một tiến trình process vào một standard input của 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 UNIX Chúng cung cấp một của việc giao tiêế 1 chiều (thuật ngữ half-duplex) giữa các process Tính năng này được dùng rộng rãi, thậm chí
trong shell của Unix
ls | sort | lp
Trang 13Half-duplex UNIX Pipes
Tạo pipes trong C
Để tạo một pipe đơn giản trong C, ta cần
dùng pipe() system call Nó cần 1 tham số, đó
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 dùng cho pipeline Sau khi tạo pipe,
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ở)
Trang 14T ạo pipes trong C
SYSTEM CALL: pipe();
PROTOTYPE: int pipe( int fd[2] );
RETURNS: 0 on success
-1 on error: errno = EMFILE (no free
descriptors)
EMFILE (system file table is full)
EFAULT (fd array is not valid)
NOTES: fd[0] is set up for reading, fd[1] is set
up for writing
Trang 15T ạo pipes trong C
Giá trị integer đầu tiên trong array được thiết 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
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 chuyển thông qua pipe đi vào kernel.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
main() {
int fd[2];
pipe(fd);
… }
Trang 16of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer); }
return(0);
}
Trang 17Các lưu ý trong half-duplex pipes
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 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
fork(), nếu không thì descriptors sẽ không được thừa
kế bởi tiến trình con !
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 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 pipe thì không
có cách nào tìm đến được nó
Trang 18Named Pipes (FIFOs)
như pipe thông thường, nhưng có một số khác biệt đáng chú ý:
Named pipes tồn tại như là một device special file trong file system
Các tiến trình khác tổ tiên có thể chia sẽ data 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 chia sẻ, named pipe còn lưu lại trong file
system để dùng sau
Trang 19Tạo FIFO
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.
mknod MYFIFO p
mkfifo a=rw MYFIFO
FIFO files có thể được xác định trong
physical file system bởi ký tự “p” ở đầu tiên:
[root@pascal root]# ls -l MYFIFO
prw-r r 1 root root 0 Aug 23 23:35 MYFIFO
[root@pascal root]#
Trang 20Tạo FIFO trong C
LIBRARY FUNCTION: mknod();
PROTOTYPE: int mknod( char *pathname, mode_t mode, dev_t dev);
RETURNS: 0 on success,
-1 on error:errno=EFAULT (pathname invalid)
EACCES (permission denied) ENAMETOOLONG (pathname too long)
ENOENT (invalid pathname) ENOTDIR (invalid pathname)
Trang 21 Muốn biến rõ hơn về mknod thì hãy dùng lệnh
man,nhưng xét ví dụ đơn giản sau trong C:
mknod("/tmp/MYFIFO", S_IFIFO|0666, 0);
Ở đây, file “/tmp/MYFIFO” được tạo ra như là 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:
Một mẹo nhỏ dùng umask() system call để tạm thời
vô hiệu giá trị umask :
Trang 22filesystem Trong ví dụ, chúng ta sẽ xem như pipe là một stream, mở nó ra với fopen() và
đóng với fclose()
Trang 25 #include <stdio.h>
#include <stdlib.h>
#define FIFO_FILE "MYFIFO"
int main(int argc, char *argv[])
Trang 27Blocking Actions on a FIFO
Thông thường, blocking xảy ra trên FIFO Nói cách khác nếu FIFO được mở để đọc, process sẽ ”block” cho đến khi một vài
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
cờ O_NONBLOCK có thể được dùng trong open() call để disable default blocking
action.
Trang 28Infamous SIGPIPE Signal
Một chú ý cuối cùng, pipes phải có một
reader và một writer Nếu một process 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
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 một pipeline.
Trang 30Introduction
Trang 31Socket
Trang 32*)&address, len);
if(result == -1) { perror("oops: client1 problem"); exit(1);
} write(sockfd, &ch, 1);
Trang 33int server_sockfd, client_sockfd;
int server_len, client_len;
*)&server_address, server_len); listen(server_sockfd, 5);
while(1) { char ch;
printf("server waiting\n"); client_sockfd =
accept(server_sockfd, (struct sockaddr
*)&client_address, &client_len); read(client_sockfd, &ch, 1); ch++;
write(client_sockfd, &ch, 1); close(client_sockfd);