- Minh họa cỏc cấu trỳc if then fi, while do done. - Tập tin timxau.sh
#!/bin/sh
echo “Chuong trinh tim xau $1 trong tap tin $2” {
wordlen=`expr length “$1”` # Do dai tu can tim
while read textline do
textlen=`expr length “$textline”` # Do dai cua dong vua doc
end=$(($textlen – wordlen + 1” index=1
while [ $index –le $end ] do
temp=`expr substr “$textline” $index $wordlen if [ “$temp” = $1 ]
then
echo “Tim thay $1 tai dong $textline” break fi index=$(($index + 1)) done done }<$2 exit 0 - Chạy chương trỡnh :
chmod a+x timxau.sh ./timxau abc bai1.txt
Bài 7 Lập trỡnh C & C++
Trỡnh biờn dịch GNU là cụng cụ phỏt triển thụng dụng nhất sẵn cú trong hệ điều hành Linux, được dựng để biờn dịch cỏc kernael của hệ điều hành. Ngoài ra gcc cung cấp cỏc thư viện và cỏc tập tin Header cần thiết để biờn dịch và chạy cỏc chương trỡnh của người dựng.
Cỏc chương trỡnh C thường cú phần tờn mở rộng là .c
Cỏc chương trỡnh C++ thường cú phần tờn mở rộng là .cc cỏc hoặc .C
Để biờn dịch và thực thi một chương trỡnh C bạn làm như sau :
1. Soạn thảo chương trỡnh. Lưu tập tin với tờn và phần mở rộng thớch hợp.
# vi example.c
2. Thoỏt vi, từ dấu nhắc hệ thống bạn gừ lệnh :
Cỳ phỏp : gcc -o filedestination filesource #gcc -o hello hello.c
3. Nếu cú lỗi, trỡnh biờn dịch sẽ thụng bỏo số thứ tự dũng lệnh lỗi. Nếu biờn dịch thành cụng, để chạy chương trỡnh gừ lệnh :
#./filedestination
Vớ dụ #./hello
Lưu ý cỏch dựng ./ trước tờn chương trỡnh, nghĩa là mỏy sẽ chỉ tỡm kiếm chương trỡnh khả thi tại thư mục hiện hành.
Để dịch cựng một lỳc nhiều tập tin chương trỡnh trong thư mục hiện hành, bạn dựng lệnh : make hoặc make all
Sau đõy là một số chương trỡnh vớ dụ : 1. Chương trỡnh hello.c #include <stdio.h> #include <math.h> main() { int i; double a; for(i=1;i<11;i++) { a=i*1.0; printf("%2d. %3d %4d %7.5f\n",i,i*i,i*i*i); } }
2. Chương trỡnh sample.c
#include <stdio.h>
void printnum ( int ); /* Khai bỏo hàm*/
void printchar ( char ); /* Khai bỏo hàm */
main () {
double tmp; /* Khai bỏo bi?n toàn c?c */
tmp = 1.234;
printf ("%f\n",tmp); /* In giỏ tr? c?a bi?n toàn c?c tmp */
printnum (5); /* In giỏ tr? s? 5 */
printf ("%f\n",tmp); /* In giỏ tr? c?a bi?n toàn c?c tmp */
printchar ('k'); /* in ký t? k */
printf ("%f\n",tmp); /* In giỏ tr? c?a bi?n toàn c?c tmp */
}
/* ộ?nh nghia hàm dú khai bỏo ? trờn */
/* Khai bỏo cỳ t? khoỏ void nghia là hàm khụng tr? v? m?t giỏ tr? */ void printnum (int inputnum) {
int tmp;
tmp = inputnum; printf ("%d \n",tmp); }
void printchar (char inputchar) {
char tmp;
tmp = inputchar; printf ("%c \n",tmp); }
Bài 8 QUẢN Lí TIẾN TRèNH 8.1. Giới thiệu
Tiến trỡnh là một mụi trường thực hiện, bao gồm một phõn đoạn lệnh và một phõn đoạn dữ liệu. Cần phõn biệt với khỏi niệm chương trỡnh chỉ gồm tập hợp lệnh.
Trờn hệ điều hành Linux, tiến trỡnh được nhận biết thụng qua số hiệu của tiến trỡnh, gọi là pid. Cũng như đối với user, nú cú thể nằm trong nhúm. Vỡ thế để phõn biệt ta nhận biết qua số hiệu nhúm gọi là pgrp. Một số hàm của C cho phộp lấy được những thụng số này:
int getpid() /* trả về giỏ trị int là pid của tiến trỡnh hiện tại*/
int getppid() /*trả về giỏ trị int là pid của tiến trỡnh cha của tiến trỡnh hiện tại */
int getpgrp() /* trả về giỏ trị int là số hiệu của nhúm tiến trỡnh*/
int setpgrp() /*trả về giỏ trị int là số hiệu nhúm tiến trỡnh mới tạo ra*/ Vớ dụ:
Lệnh : printf("Toi la tien trinh %d thuoc nhom %d",getpid(),getgrp());
Kết quả sẽ là: Toi là tien trinh 235 thuoc nhom 231
8.1.1. Tạo một tiến trỡnh - lệnh fork
int fork() tạo ra một tiến trỡnh con. Giỏ trị trả lại là 0 cho tiến trỡnh con và dấu
hiệu pid cho tiến trỡnh cha. Giỏ trị sẽ là -1 nếu khụng tạo được tiến trỡnh mới.
Theo nguyờn tắc cơ bản của hệ thống, tiến trỡnh con và cha sẽ cú cựng đoạn mó. Đoạn dữ liệu của tiến trỡnh mới là một bản sao chộp chớnh xỏc đoạn dữ liệu của tiến trỡnh cha. Tuy nhiờn tiến trỡnh con vẫn khỏc tiến trỡnh cha ở pid, thời gian xử lý, ...
8.1.2. Dừng một tiến trỡnh
Lệnh kill của Shell cú thể dựng để chấm dứt hoạt động của một tiến trỡnh. vớ dụ như khi muốn dừng tiến trỡnh 234 ta dựng lệnh: kill 234
C cũng cú lệnh kill như sau:
int kill(pid, sig);
int pid; là dấu hiệu nhận biết của một tiến trỡnh.
8.1.3. Giao tiếp giữa cỏc tiến trỡnh
Việc giao tiếp giữa cỏc tiến trỡnh được thực hiện thụng qua cỏc tớn hiệu chuẫn của hệ thống. Tớn hiệu là một sự ngắt quóng logic được gửi đến cỏc tiến trỡnh bởi hệ thống để thụng bỏo cho chỳng về những sự việc khụng bỡnh thường trong mụi trường hoạt động của chỳng (như lỗi bộ nhớ, lỗi vào ra). Nú cũng cho phộp cỏc tiến trỡnh liờn lạc với nhau. Một tớn hiệu (trừ SIGKILL) cú thể được xem xột theo ba cỏch khỏc nhau: 1. Tiến trỡnh cú thể được bỏ qua: Vớ dụ chương trỡnh cú thể bỏ qua sự ngắt quóng của người sử dụng hệ thống (đú là sự bỏ qua khi một tiến trỡnh đang được sử dụng ở phần nền.
2. Tiến trỡnh cú thể được thực hiện: Trong trường hợp này, khi nhận được 1 tina stiệu, việc thực hiện 1 tiến trỡnh được chuyển về một quy trỡnh do người sử dụng xỏc định trước, sau đú trở lại nơi nú bị ngắt.
3. Lỗi cú thể được tiến trỡnh trả về sau khi nhận được tớn hiệu này. Dưới đõy là một số tớn hiệu thường gặp:
SIGHUP Tớn hiệu này được phỏt đến cỏc tiến trỡnh vào lỳc cuối khi mà nú tự ngắt. Nú cũng được phỏt đến mọi tiến trỡnh cú tiến trỡnh chớnh tự ngắt.
SIGINT Tớn hiệu này được phỏt đến cỏc tiến trỡnh khi ta ra lệnh ngắt.
SIGQUIT Tương tự như trờn khi ta gừ vào ^D.
SIGILL
Lệnh khụng hợp lệ, tớn hiệu được phỏt ra khi phỏt hiện 1 lệnh khụng đỳng ở cấp độ vật lý (vớ dụ như 1 tiến trỡnh thực hiện một lệnh mà mỏy tớnh chụng cú lệnh này).
SIGTRAP Tớn hiệu được phỏt ra sau mỗi lệnh trong trường hợp tiến trỡnh cú sử dụng lệnh ptrace().
SIGIOT Bẫy được phỏt khi cú cỏc vấn đề về vật lý.
SIGEMT Bẫy của lệnh phỏt, được phỏt ra khi cú lỗi vật lý trong khi thực hiện.
SIGFPE Được phỏt ra khi cú lỗi về tớnh toỏn như một số cú dấu phẩy nối cú định dạng khụng hợp lý. Gần như luụn chỉ ra lỗi khi lập trỡnh.
SIGKILL Trang bịđể kết thỳc tiến trỡnh. Khụng thể bỏ qua hoặc cắt tớn hiệu này.
SIGBUS Được phỏt khi gặp lỗi trờn bus.
SYSGEGV Được phỏt ra khi gặp lỗi trờn phõn đoạn sự truy cập dữ liệu bờn ngoài phõn đoạn dữ liệu được cấp phỏt cho tiến trỡnh.
SIGSYS Đối số khụng đỳng cho hệ thống gọi.
SIGPIPE Viết trờn một ống dẫn khụng mởđểđọc.
SIGALRM Phỏt ra khi đồng hồ của một tiến trỡnh ngừng lại. Đồng hồđược hoạt động bằng lệnh alrm().
SIGTERM Được phỏt ra khi một tiến trỡnh kết thỳc bỡnh thường. Cũng cú thể dựng để dừng 1 hệ thống để kết thỳc tất cả cỏc tiến trỡnh hoạt động.
8.1.4. Liờn lạc giữa hai tiến trỡnh
Từ một chương trỡnh đơn giản dưới đõy sử dụng cỏc lệnh phỏt và nhận tớn hiệu, sau đú giỳp liờn lạc giữa hai tiến trỡnh.
Nội dung của vớ dụ là sự liờn lạc giữa một tiến trỡnh cha và một tiến trỡnh con thụng qua cỏc tớn hiệu đó được trỡnh bày phần trước.
#include <errno. h> #include <signal. h> void fils_atc()
{
printf(" Tien trinh bi loai bo !!!\n"); kill(getpid(), SIGINT); } /***********************************/ void fils() { signal(SIGUSR1, fils_atc);
printf(" Hinh thanh tien trinh moi. Nhung chuan bi loai bo tien trinh nay !!\n"); while(1);
}
/******************************/ main()
{
int ppid, pid;
if ((pid = fork())==0) fils(); else
{
sleep(3);
printf(" Chap nhan !! Tien trinh se bi loai bo.\n"); kill(pid, SIGUSR1);
} }
Trong vớ dụ trờn, tiến trỡnh con cú sử dụng hàm signal(SIGUSR1, fils_atc). Hàm này cú tỏc dụng mỗi khi tiến trỡnh con nhận được tớn hiệu SIGUSR1 thỡ hàm fils_atc() sẽ được thực thi.
Như vậy ở vớ dụ trờn một tiến trỡnh con đó được tạo ra nhưng nú lại khụng muốn tiếp tục tồn tại. Do vậy sau khi tạm dừng lại sleep(3), tiến trỡnh cha đó gởi đến cho tiến trỡnh con một tớn hiệu là SIGUSR1 bằng lệnh:
kill(pid, SIGUSR1);
ở tiến trỡnh con, tớn hiệu SIGUSR1 đó được gỏn với hàm fils_atc(). Hàm này ra một thụng bỏo bỏo hiệu tiến trỡnh này sắp chết rồi tự gởi đến chớnh mỡnh (tiến trỡnh con) tớn hiệu SIGINT, tớn hiệu ngắt tiến trỡnh. Và tiến trỡnh con đó chết.
kill(getpid(), SIGINT);
Một số nhược điểm khi liờn lạc trực tiếp bằng tớn hiệu:
- Một tớn hiệu cú thể bị bỏ qua, kết thỳc một tiến trỡnh hoặc bị chặn lại. Đú là lý do chớnh đưa ra cỏc tớn hiệu khụng thớch ứng được để tiến hành liờn lạc giữa cỏc tiến trỡnh. Một thụng điệp điệp dưới hỡnh thức tớn hiệu cú thể sẽ bị mất nếu nú được nhận lỳc loại tớn hiệu này tạm thời bị bỏ qua.
- Một vấn đề khỏc là cỏc tớn hiệu cú quyền rất lớn, khi đến chỳng làm ngắt quóng cụng việc hiện tại. Vớ dụ việc nhận một tớn hiệu trong khi tiến trỡnh đang đợi một sự kiện (mà cú thể đến khi sử dụng cỏc lệnh open(), read(), ...) làm cho việc thực thi hàm bị chệch hướng. Khi trở lại, lệnh chớnh bị ngắt gởi lại một thụng điệp bỏo lỗi mà hoàn toàn khụng xử lý được.
Ngoài việc liờn lạc trực tiếp như ở vớ dụ trờn, cũn cho phộp một phương phỏp liờn lạc giữa cỏc tiến trỡnh khỏc, đú là liờn lạc qua "đường ống".
8.2. Lập trỡnh đa tiến trỡnh
8.2.1. ống dẫn liờn lạc
ống dẫn là một cơ chế cơ bản để liờn lạc giỏn tiếp giữa cỏc tiến trỡnh. Đú là cỏc file đặc biệt (FIFO), ở đú cỏc thụng tin được truyền đi 1 đầu và thoỏt ra ở một đầu khỏc.
Một số đặc điểm của "ống dẫn":
- Cỏc ống dẫn chỉ mang tớnh chất tạm thời, chỉ tồn tại trong thời gian thực hiện của một tiến trỡnh tạo ra nú.
- Muốn tạo ra một ống dẫn phải bắt đầu bằng một lệnh đặc biệt: pipe().
- Nhiều tiến trỡnh cú thể viết và đọc trờn cựng một ống dẫn. Tuy nhiờn, khụng cú một cơ chế nào để phõn biệt thụng tin cho cỏc tiến trỡnh ở đầu ra.
- Dung lượng ống dẫn bị hạn chế (khoảng 4KB). Do đú khi chỳng ta cố gắng viết khi ống dẫn bị đầy thỡ sẽ gặp phải trường hợp tắc nghẽn.
- Cỏc tiến trỡnh liờn lạc qua ống dẫn phải cú mối quan hệ họ hàng và cỏc ống dẫn nối phải được mở trước khi tạo ra cỏc tiến trỡnh con.
- Khụng thể tự thay đổi vị trớ thụng tin trong ống.
8.2.2. Thao tỏc với "ống dẫn liờn lạc"
Tạo một ống dẫn:
int p_desc[2]; int pipe(p_desc);
Giỏ trị trả về là 0 nếu thành cụng, -1 nếu thất bại.
p_desc[0] : chứa cỏc số hiệu mụ tả nhờ đú cú thể đọc trong ống dẫn. p_desc[1] : chứa cỏc số hiệu mụ tả nhờ đú cú thể viết trong ống dẫn.
Như vậy việc viết trong p_desc[1] là để truyền dữ liệu trong ống và việc đọc trong p_desc[0] để nhận chỳng. Vớ dụ: #include <errno. h> #include <signal. h> main() {
int i,ret, p_desc[2];
char c; pipe(p_desc);
write(p_desc[1], "AB", 2); for (i=1; i<=3,i ++) {
ret=read(p_desc[0], &c, 1); if (ret == 1)
printf(" Gia tri: %c\n",c); else
perror("Loi ong dan rong"); }
}
Vớ dụ trờn chỉ ra rằng ta cú thể truyền và nhận thụng tin trờn ống dẫn. Chỳng ta đó dựng hàm read() và write() để viết (truyền) và đọc (nhận) trờn ống dẫn.
8.2.3. Liờn lạc giữa tiến trỡnh cha và tiến trỡnh con
Trong vớ dụ dưới đõy, một tiến trỡnh tạo ra một ống dẫn, tạo ra một tiến trỡnh con, viết một văn bản vào ống dẫn.Tiến trỡnh con thừa hưởng ống dẫn và cỏc ký hiệu mụ tả của ống dẫn, thực hiện đọc trong ống dẫn:
#include <errno. h> #include <stdio. h>
void code_fils(int number) {
int fd, nread;
fd=number;
printf(" So hieu mo ta la %d\n",fd);
switch (nread=read(fd, texte, sizeof(texte))) { case -1: perror("Loi doc."); case 0: perror("EOF"); default:
printf("Van ban nhan duoc co %d ky tu: %s\n",fd, texte); } } main() { int fd[2]; char chaine[10]; if (pipe(fd)==-1)
{ perror("Loi khoi tao pipe."); exit(1);
}
switch (fork()) { case -1:
perror(" Loi khoi tao tien trinh."); break; case 0: if (close(fd[1])==-1) perror(" Error."); code_fils(fd[0]); exit(0); } close(fd[0]); if (write(fd[1]),"hello",6)==-1) perror("Loi truyen."); } Kết quả chương trỡnh: So hieu mo ta la: 5
Van ban nhan duoc co 6 ky tu: hello
Chỳ ý rằng, tiến trỡnh con đọc trong ống dẫn mà khụng viết ở đú nờn nú bắt đầu bằng cỏch đúng phần viết fd[1] để tiết kiệm cỏc tớn hiệu mụ tả của tổ hợp. Tương tự, vỡ tiến trỡnh cha chỉ sử dụng phần viết nờn nú đúng phần đọc lại (fd[0]). Sau đú tiến trỡnh cha viết vào ống dẫn 6 ký tự và tiến trỡnh con đó đọc chỳng.
Bài 9 Lập trỡnh mạng TCP/IP
9.1. Lập trỡnh client /server theo giao thức TCP/IP
• Chương trỡnh tcpClient.c
/* Khai bỏo cỏc file thư viện cần thiết để gọi hàm socket*/
#include <sys/types.h> #include <sys/socket.h>
#include <netinet/in.h> /*gethostbyname*/
#include <arpa/inet.h> #include <netdb.h> #include <stdio.h>
#include <unistd.h> /* close */
#define SERVER_PORT 1500 #define MAX_MSG 100
int main (int argc, char *argv[]) {
/* Khởi tạo cỏc biến dựng trong chương trỡnh */
int sd, rc, i;
struct sockaddr_in localAddr, servAddr; struct hostent *h;
if(argc < 3) {
printf("usage: %s <IPserver> <data1> <data2> ... <dataN>\n",argv[0]); exit(1);
}
/* Hàm gethostbyname() lấy về địa chỉ IP theo tờn nhập vào trong tập tin /etc/hosts */
h = gethostbyname(argv[1]); if(h==NULL) {
printf("%s: unknown host '%s'\n",argv[0],argv[1]); exit(1);
}
servAddr.sin_family = h->h_addrtype;
memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); servAddr.sin_port = htons(SERVER_PORT);
/* Gỏn cỏc giỏ trị cho đối tượng socket.
Tạo socket cho mỏy Client. Lưu lại số mụ tả socket */
sd = socket(AF_INET, SOCK_STREAM, 0); if(sd<0) {
perror("cannot open socket "); exit(1);
}
/* Đặt tờn socket cho chương trỡnh Client
Gỏn địa chỉ kết nối cho socket theo giao thức Internet */
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY); localAddr.sin_port = htons(0);
/* Hàm htons() dựng để chuyển đổi trật tự byte của số nguyờn trước khi gởi đi – do hệ thống sử dụng cơ chế giao tiếp TCP/IP */
rc = bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr)); if(rc<0) {
printf("%s: cannot bind port TCP %u\n",argv[0],SERVER_PORT); perror("error ");
exit(1); }
/* Thực hiện kết nối đến server theo tờn/địa chỉ nhập vào từ dũng lệnh */
rc = connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr)); if(rc<0) {
perror("cannot connect "); exit(1);
}
/* Sau khi socket đó kết nối, thực hiện gửi cỏc dữ liệu đến chương trỡnh Server */
for(i=2;i<argc;i++) {
rc = send(sd, argv[i], strlen(argv[i]) + 1, 0);
if(rc<0) {
perror("cannot send data "); close(sd);
exit(1);
}/* if */
printf("%s: data%u sent (%s)\n",argv[0],i-1,argv[i]);
}/* for */
return 0; }/*main*/
• Chương trỡnh tcpServer.c
/* Chuong trinh tcpServer.c */
/* Khai bỏo cỏc file thư viện cần thiết để gọi hàm socket*/
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h>
#include <unistd.h> /* close */
#define SUCCESS 0 #define ERROR 1 #define END_LINE 0x0 #define SERVER_PORT 1500 #define MAX_MSG 100 /* function readline */ int read_line();
int main (int argc, char *argv[]) {
int sd, newSd, cliLen;
struct sockaddr_in cliAddr, servAddr; char line[MAX_MSG];
/* Gỏn cỏc giỏ trị cho đối tượng socket.
Tạo socket cho mỏy Server. Lưu lại số mụ tả socket */
sd = socket(AF_INET, SOCK_STREAM, 0); if(sd<0) {
perror("cannot open socket "); return ERROR;
}
/* Đặt tờn socket cho chương trỡnh Server
Gỏn địa chỉ kết nối cho socket theo giao thức Internet */ servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY); servAddr.sin_port = htons(SERVER_PORT);
if(bind(sd, (struct sockaddr *) &servAddr, sizeof(servAddr))<0) { perror("cannot bind port ");
return ERROR; }
/* Tạo hàng đợi lắng nghe kết nối của client
Cho phộp hàng đợi nhận tối đa 5 kết nối */ listen(sd,5);
/* Lặp liờn tục chờ và lxy kết nối của client */
while(1) {
printf("%s: waiting for data on port TCP %u\n",argv[0],SERVER_PORT); cliLen = sizeof(cliAddr);
/* Chấp nhận kết nối */
newSd = accept(sd, (struct sockaddr *) &cliAddr, &cliLen); if(newSd<0) {
perror("cannot accept connection "); return ERROR; } /* init line */ memset(line,0x0,MAX_MSG);
/* Đọc dữ liệu do Client gởi đến - xử lý dữ liệu nhận được */ while(read_line(newSd,line)!=ERROR) {