Cách tạo Daemon trên Linux
Daemon là các quy trình không chạy trực tiếp dưới sự kiểm soát của người dùng mà chạy ở chế độ nền. Thông thường, chúng bắt đầu khi khởi động hệ thống và chạy liên tục cho đến khi hệ thống tắt. Sự khác biệt duy nhất giữa các quy trình này và quy trình bình thường là chúng không gửi tin nhắn đến bảng điều khiển hoặc màn hình theo bất kỳ cách nào.
Đây là cách bạn có thể tạo một daemon trên máy Linux.
Mục Lục
Giới thiệu ngắn gọn về cách Daemons được tạo ra
Rất nhiều daemon chạy trên hệ thống và một số ví dụ daemon quen thuộc như sau:
- crond: Làm cho các lệnh chạy tại thời điểm được chỉ định
- sshd: Cho phép đăng nhập vào hệ thống từ các máy từ xa
- httpd: Cung cấp các trang web
- nfsd: Cho phép chia sẻ tệp qua mạng
Ngoài ra, các quy trình daemon thường được đặt tên để kết thúc bằng chữ cái dmặc dù nó không bắt buộc.
Để quá trình chạy dưới dạng daemon, hãy làm theo đường dẫn sau:
- Các hoạt động ban đầu, chẳng hạn như đọc tệp cấu hình hoặc lấy tài nguyên hệ thống cần thiết, phải được thực hiện trước khi quá trình trở thành daemon. Bằng cách này, hệ thống có thể báo cáo các lỗi đã nhận cho người dùng và quá trình sẽ được kết thúc bằng một mã lỗi thích hợp.
- Một tiến trình chạy nền được tạo với init là tiến trình mẹ của nó. Với mục đích này, một quy trình con được tách từ quy trình init trước, và sau đó quy trình trên được kết thúc với lối ra.
- Một phiên mới sẽ mở bằng cách gọi hàm setid và quá trình này sẽ được ngắt kết nối khỏi thiết bị đầu cuối.
- Tất cả các bộ mô tả tệp đang mở được kế thừa từ quy trình mẹ đều bị đóng.
- Thông báo đầu vào, đầu ra và lỗi tiêu chuẩn được chuyển hướng đến / dev / null.
- Thư mục làm việc của tiến trình phải thay đổi.
Phiên bản Daemon là gì?
Sau khi đăng nhập vào hệ thống thông qua thiết bị đầu cuối, người dùng có thể chạy nhiều ứng dụng thông qua chương trình shell. Các quy trình này sẽ đóng khi người dùng thoát khỏi hệ thống. Hệ điều hành nhóm các quy trình này thành các nhóm phiên và quy trình.
Mỗi phiên bao gồm các nhóm quy trình. Bạn có thể mô tả tình huống này như sau:
Thiết bị đầu cuối nơi các quá trình nhận đầu vào và gửi đầu ra của chúng được gọi là thiết bị đầu cuối điều khiển. Một thiết bị đầu cuối điều khiển chỉ được liên kết với một phiên tại một thời điểm.
Một phiên và các nhóm quy trình trong đó có số nhận dạng (ID); các số nhận dạng này là số nhận dạng quy trình (PID) của các trưởng nhóm phiên và quy trình. Một quy trình con chia sẻ cùng một nhóm với quy trình mẹ của nó. Khi nhiều quy trình đang giao tiếp với cơ chế ống, quy trình đầu tiên sẽ trở thành thủ lĩnh nhóm quy trình.
Tạo quy trình Daemon trên Linux
Ở đây bạn sẽ thấy cách bạn có thể tạo một hàm daemon. Với mục đích này, bạn sẽ tạo một hàm có tên _daemon. Bạn có thể bắt đầu bằng cách đặt tên mã ứng dụng sẽ chạy dưới dạng daemon như test.cvà mã mà bạn sẽ tạo hàm daemon dưới dạng daemon.c.
#include <stdio.h>
int _daemon(int, int);
int main()
{
getchar();
_daemon(0, 0);
getchar();
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#include <linux/limits.h>
int _daemon(int nochdir, int noclose) {
pid_t pid;
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
return 0;
}
Để tạo một daemon, bạn cần một quy trình nền có quy trình mẹ là init. Trong đoạn mã trên, _daemon tạo một tiến trình con và sau đó giết tiến trình cha. Trong trường hợp này, quy trình mới của bạn sẽ là một quy trình con của init và sẽ tiếp tục chạy ở chế độ nền.
Bây giờ, hãy biên dịch ứng dụng bằng lệnh sau và kiểm tra trạng thái của quá trình trước và sau _deamon được gọi là:
gcc -o test test.c daemon.c
Chạy ứng dụng và chuyển sang một thiết bị đầu cuối khác mà không cần nhấn bất kỳ phím nào khác:
./test
Bạn có thể thấy rằng các giá trị liên quan đến quy trình của bạn như sau. Tại đây, bạn sẽ phải sử dụng lệnh ps để nhận thông tin liên quan đến quy trình. Trong trường hợp này, _daemon chức năng vẫn chưa được gọi.
ps -C test -o "pid ppid pgid sid tty stat command"
PID PPID PGID SID TT STAT COMMAND
10296 5119 10296 5117 pts/2 S+ ./test
Khi bạn nhìn vào STAT bạn thấy rằng quy trình của bạn đang chạy nhưng đang đợi một sự kiện ngoài lịch trình xảy ra sẽ khiến nó chạy ở chế độ nền trước.
Viết tắt | Nghĩa |
S | Đang ngủ chờ một sự kiện xảy ra |
T | Ứng dụng đã dừng |
S | Người lãnh đạo phiên họp |
+ | Ứng dụng đang chạy ở nền trước |
Bạn có thể thấy rằng quy trình chính của ứng dụng của bạn là trình bao như mong đợi.
ps -jp 5119
PID PGID SID TTY TIME CMD
5119 5119 5117 pts/2 00:00:02 zsh
Bây giờ, hãy quay lại thiết bị đầu cuối nơi bạn đang chạy ứng dụng của mình và nhấn đi vào để gọi ra _daemon hàm số. Sau đó, xem lại thông tin quy trình trên thiết bị đầu cuối khác.
ps -C test -o "pid ppid pgid sid tty stat command"
PID PPID PGID SID TT STAT COMMAND
22504 1 22481 5117 pts/2 S ./test
Trước hết, bạn có thể nói rằng quy trình con mới đang chạy trong nền vì bạn không thấy + nhân vật trong STAT đồng ruộng. Bây giờ hãy kiểm tra xem ai là tiến trình mẹ của tiến trình bằng cách sử dụng lệnh sau:
ps -jp 1
PID PGID SID TTY TIME CMD
1 1 1 ? 00:00:01 systemd
Bây giờ bạn có thể thấy rằng quy trình chính của quy trình của bạn là systemd tiến trình. Ở trên đã đề cập rằng đối với bước tiếp theo, một phiên mới sẽ mở ra và quá trình này nên được ngắt kết nối khỏi thiết bị đầu cuối điều khiển. Đối với điều này, bạn sử dụng hàm setid. Thêm cuộc gọi này vào của bạn _daemon hàm số.
Đoạn mã cần thêm như sau:
if (setsid() == -1)
return -1;
Bây giờ bạn đã kiểm tra trạng thái trước đây _daemon đã gọi, bây giờ bạn có thể xóa cái đầu tiên getchar chức năng trong test.c mã số.
#include <stdio.h>
int _daemon(int, int);
int main()
{
_daemon(0, 0);
getchar();
return 0;
}
Sau khi biên dịch và chạy lại ứng dụng, hãy chuyển đến thiết bị đầu cuối nơi bạn đã đánh giá. Trạng thái mới của quy trình của bạn như sau:
ps -C test -o "pid ppid pgid sid tty stat command"
PID PPID PGID SID TT STAT COMMAND
25494 1 25494 25494 ? Ss ./test
Các ? đăng nhập TT trường cho biết rằng quy trình của bạn không còn được kết nối với thiết bị đầu cuối. Lưu ý rằng PID, PGIDvà SID các giá trị của quy trình của bạn giống nhau. Quy trình của bạn bây giờ là một người dẫn đầu phiên.
Trong bước tiếp theo, thay đổi thư mục làm việc thành thư mục gốc theo giá trị của đối số bạn đã truyền. Bạn có thể thêm đoạn mã sau vào _daemon chức năng này:
if (!nochdir) {
if (chdir("/") == -1)
return -1;
}
Bây giờ, theo đối số được truyền, có thể đóng tất cả các bộ mô tả tệp. Thêm mã sau vào _daemon hàm số:
if (!noclose) {
for (i = 0; i < NR_OPEN; i++)
close(i);
open("/dev/null", O_RDWR);
dup(0);
dup(0);
}
Sau khi tất cả các bộ mô tả tệp được đóng, các tệp mới được mở bởi daemon sẽ được hiển thị với các bộ mô tả 0, 1 và 2 tương ứng. Trong trường hợp này, ví dụ, printf các lệnh trong mã sẽ được chuyển hướng đến tệp thứ hai được mở. Để tránh điều này, ba số nhận dạng đầu tiên trỏ đến / dev / null thiết bị.
Trong trường hợp này, trạng thái cuối cùng của _daemon chức năng sẽ như sau:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
int _daemon(void) {
pid_t pid, sid;
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// Create a SID for child
sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while (1) {
sleep(30);
}
exit(EXIT_SUCCESS);
}
Đây là một ví dụ về đoạn mã chạy sshd ứng dụng như một daemon:
...
if (!(debug_flag || inetd_flag || no_daemon_flag)) {
int fd;
if (daemon(0, 0) < 0)
fatal("daemon() failed: %.200s", strerror(errno));
fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
if (fd >= 0) {
(void) ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
}
...
Daemon rất quan trọng đối với lập trình hệ thống Linux
Daemons là các chương trình thực hiện các hành động khác nhau theo cách thức xác định trước được thiết lập để phản ứng với các sự kiện nhất định. Chúng chạy âm thầm trên máy Linux của bạn. Chúng không chịu sự kiểm soát trực tiếp của người dùng và mỗi dịch vụ chạy nền đều có daemon của nó.
Điều quan trọng là phải nắm vững các daemon để tìm hiểu cấu trúc hạt nhân của hệ điều hành Linux và hiểu hoạt động của các kiến trúc hệ thống khác nhau.
Đọc tiếp
Giới thiệu về tác giả