Cに基づいて、Linuxカーネル関数、システムレベル関数をさらに研究する
2017年8月
1.知識
1.1基本的な概念:
(1)プログラム
は、ディスクに格納された実行可能ファイルです。
(2)プロセス
はプログラムの実行インスタンスです。各プロセスには、独立した権限と責任があり、独自の仮想アドレス空間で実行されます。
プロセスは互いに影響しませんが、通信することはできます。
(3)プロセスID。PIDと呼ばれるプロセスIDは、
負でない整数であり、プロセスのデジタル識別子です。
1.2起動ルーチン
(1)「起動ルーチン」がコンパイルされ、メインの前に実行されます。
(2)コマンドラインパラメータを収集し、それらをメインのargcおよびargvに渡します。そして、環境テーブルenvp。
(3)「終了関数」atexit()を登録します。
1.3プロセスの終了
(1)正常終了
- メイン関数から戻る(return)
- 呼び出し出口(標準Cライブラリー関数)
- _exitまたは_Exit(システムコール)を呼び出す
- 最後のスレッドは「開始ルーチン」から戻ります
- 最後のスレッドがpthread_exitを呼び出す
注意:
returnおよびexit()は、標準IOキャッシュを更新し、終了関数を自動的に呼び出します。
_exit()および_Exit()。更新も呼び出しもされません。
(2)異常終了
- コールアボート
- 信号を受信して終了する
- 最後のスレッドは「キャンセル」リクエストに応答します
(3)プロセスリターン
- 通常、プログラムは正常に0を返し、それ以外の場合はゼロ以外を返します
- シェルでは、プロセスの戻り値を生成できます(echo $?)
(4)ターミネーション機能
#include <stdio.h>
int atexit(void (*function)(void));
向内核登记终止函数,成功返回0,否则-1
- 開始された各プロセスは、デフォルトで標準の終了関数を登録します
- 終了関数は、プロセスが終了すると、そのプロセスが占有していたいくつかのリソースを解放します
- 複数の終了関数を登録すると、実行シーケンスがスタックで実行され、最初に登録されてから実行されます。
1.4回路図
1.5プロセスのリソース制限
Linuxで利用可能なリソースは次のとおりです。
RLIMIT_AS 进程可用的存储区大小
RLIMIT_CORE core文件最大字节数
RLIMIT_CPU CPU时间最大值
RLIMIT_DATA 数据段最大长度
RLIMIT_FSIZE 可创建文件的最大长度
RLIMIT_LOCKS 文件锁的最大数
RLIMIT_MEMLOCK使用mlock能否在存储器中锁定的最长字节数
RLIMIT_NOFILE 能打开的最大文件数
RLIMIT_NPROC 每个用户ID可拥有的最大子进程数
RLIMIT_RSS 最大驻内存集的字节长度
RLIMIT_STACK 栈的最大长度
- ヘッドファイル
#include <sys/resource.h>
struct rlimit{
rlim_t rlim_cur;/*软件限制:当前限制*/
rlim_t rlim_max;/*硬件限制:当前限制可以达到的最大值*/
}
- 関数
(1)获取进程的资源限制,存放在rlptr指向 的结构体中。成功返回0,失败非0。
int getrlimit(int resource , struct rlimit *rlptr);
(2)修改resource指定的资源限制,通过rlptr指向的结构体。成功返回0
int setlimit(int resource,const struct rlimie *rlptr);
- 構成ファイル
(1)/etc/security/limits.conf
(2)Linuxでは、プロセスリソースの初期化はプロセス0によって確立され、後続のプロセスによって継承されます。 - リソース制限の変更規則
(1)ハードウェアリソース制限は、ソフトウェア制限以上である必要があります。
(2)どのプロセスでもソフトウェアリソース制限を増減できますが、ソフトウェア制限より大きくなければなりません。通常のユーザーはこの操作を元に戻すことはできません。
(3)スーパーユーザーはハードウェア制限を増やすことができます。
2.プロセスに関連する指示
2.1 PS命令
プロセスID(PID)、プロセスユーザーID、プロセスステータスSTAT、プロセスコマンドなどを表示できます。
3.プロセスの一般的なステータス
3.1稼働状況
システム
レディステートプロセスの現在のプロセス
PSコマンドstat列== R
3.2待機状態
イベントが発生するのを
待つシステムリソース
PSコマンドのstat列を待つ== S
3.3停止状態
PSコマンドのstat列== T
3.4ゾンビのステータス
プロセスが終了または終了し
ます。プロセステーブルエントリに
PSコマンドを記録するstat列がまだあります== Z
3.5プロセスステータスの変換関係
4.プロセススケジューリング
4.1一般的な手順
(1)カーネルで作業を処理する
(2)現在のプロセスを処理する
(3)プロセスを選択する(リアルタイムプロセスと通常プロセス)
(4)プロセス交換
4.2 task_structでの情報のスケジューリング
(1)戦略
- ローテーション戦略
- 先入れ先出し戦略
(2)優先順位
- 変数jiffies
(3)リアルタイム優先度
- リアルタイムプロセス間
(4)カウンター
5.プロセスID
プロセスには
、現在のプロセスID、実際のユーザーID、実効ユーザーID、ユーザーグループID、親プロセスID、プロセスグループIDなど、多くの識別子があります。
これに関連する機能:
- ヘッドファイル
#include <unistd.h>
#include <sys/types.h>
- プロセスIDを取得する関数
pid_t getpid(void); //获取当前进程的ID标识
pid_t getppid(void); //获取父进程的ID标识
pid_t getpgrp(void); //获取当前进程所在的进程组ID标识。
pid_t getpgid(pid_t pid); //获取指定ID的进程所在的进程组ID标识。
uid_t getuid(void); //获取当前进程的 实际用户ID
uid_t geteuid(void); //获取当前进程的 有效用户ID
gid_t getgid(void); //获取当前进程的用户组ID
6.プロセスの作成
この記事の主要な内容。
6.1子プロセスを作成するための関数フォーク
-
fork関数
forkによって作成された新しいプロセスは、子プロセスと呼ばれます。この関数は1回呼び出され、2回返されます。
2回返すことの違い:
(1)親プロセスでは、新しい子プロセスのプロセスIDが返されます。
(2)新しい子プロセスでは、0が返されます。子プロセスのデータセグメント、ヒープ、スタックが再作成されるためです。
(3)親プロセスと子プロセスの実行順序は、システムのスケジューリングに従って自動的に決定されます。
(4)子プロセスは親プロセスのメモリ空間をコピーします。 -
vfork関数
はforkに似ていますが、子プロセスが最初に実行され、親プロセスのメモリ空間はコピーされません。 -
子プロセスの継承された属性
- ユーザー情報と権限
- ディレクトリ情報、信号情報、環境、リソース制限
- 共有メモリセグメント、ヒープ、スタックおよびデータセグメント、共有コードセグメント
- 子プロセスの固有の属性
- プロセスID
- ロック情報
- 営業時間
- 保留中の信号
- ファイル操作時にカーネル構造が変化する
- 子プロセスはファイル記述テーブルを継承しますが、ファイルテーブルエントリとiノードは継承しませんが共有します。
- 子プロセスが作成されると、ファイルテーブルエントリの参照カウンタが1増加して2になります。親プロセスがクローズ操作を実行すると、カウンタが1減少します。子プロセスは引き続きファイルテーブルエントリを使用できます。カウンタが0の場合のみ、ファイルエントリが解放されます。
6.2プロセス寄生exec関数クラスター
- exec関数は、別のプログラムを実行するために使用されます。新しく実行されたプログラムは、元のプロセスのテキスト、データ、ヒープ、スタックを置き換えます。
- execは新しいプロセスを作成しません。プロセスIDは変更されていません。
- forkが子プロセスを作成した後、exec関数を使用して、子プロセスで別のプログラムを実行できます。
- ヘッドファイル
#include <unistd.h>
2.機能
//list 列出每个字符参数
int execl(const char *pathname,const char *arg0, ... /*(char*)0*/);
//argv 字符数组
int execv(const char *pathname,char * const argv[]);
//list 列出每个字符参数,环境表
int execle(const char *pathname,const char *arg0, ... /*(char*)0,char* const envp[]*/);
//argv 字符数组,环境表
int execve(const char *pathname,char * const argv[],char* const envp[]);
//
int execlp(const char *pathname,const char *arg0, ... /*(char*)0*/);
//
int execvp(const char *pathname,char * const argv[]);
上述所有的返回:出错返回-1 ,成功不返回。
6.3システム関数
- システム関数は子プロセスの内部コンポーネントであり、exec関数は子プロセスによって呼び出されます。
- ヘッドファイル
#include <stdlib.h>
2.機能
简化exec函数的使用,成功返回执行命令的状态,错误返回-1
int system(const char * command);
7.いくつかの特別なプロセス
7.1デーモン
- デーモンは、寿命が長いプロセスです。多くの場合、システムの起動時に開始し、システムのシャットダウン時に終了します。
- すべてのデーモンは、スーパーユーザーの優先順位(ユーザーIDは0)で実行されます。
- デーモンは端末を制御しません。
- デーモンの親プロセスはinitプロセスです。
7.2孤立プロセス
- 親プロセスが終了すると、子プロセスは孤立プロセスになります。プロセス1(初期プロセス)で採用。
7.3ゾンビプロセス
- 子プロセスは終了しますが、メモリは完全には解放されません(カーネルのtask_structは解放されません)。このプロセスはゾンビプロセスになります。
- ゾンビプロセスの親プロセスが終了すると、最初のプロセス(初期プロセス)によって採用され、最後にリサイクルされます。
- ゾンビプロセスを回避する方法:
(1)ゾンビプロセスの親プロセスをリサイクルさせます。親プロセスは、子プロセスが終了してリサイクルするかどうかを定期的にチェックします。wait()またはwaitpid()を呼び出して、ゾンビプロセスを解放するようカーネルに通知します。
(2)シグナルSIGCHLDを使用して処理を通知します。そして、シグナルハンドラでwait()を呼び出します。
(3)ゾンビプロセスを孤立プロセスにして、initプロセスでリサイクルさせます。
- ヘッドファイル
#include <sys/types.h>
#include <sys/wait.h>
- 関数
(1)等待子进程退出并回收。防止僵尸进程。成功返回子进程ID,出错返回-1。
pid_t wait(int *status);
(2)wait函数的非阻塞版本。成功返回子进程ID,出错返回-1。
pid_t waitpid(pid_t pid , int * status ,int options);
1)pid参数。
pid==-1 //等待任一子进程,功能和wait等效。
pid== 0 //等待 同进程组ID的任一子进程。
pid > 0 //等待指定的子进程
pid < -1 //等待 组ID等于(-pid)的任一子进程。
2)status参数。为空时,等待回收【任意状态】结束的子进程。不为空,则等待【指定状态】结束的子进程。
检查wait和waitpid函数返回终止状态的宏
WIFEXITED/WEXITSTATUS(status) //若为正常终止子进程返回的状态,则为真。
WIFSIGNALED/WTERMSIG(status) //若为异常终止子进程返回的状态,则为真。(接到一个不能捕捉的信号)
WIFSTOPED/WSTOPSIG(status) //若为当前暂停子进程返回的状态,则为真。
3)options参数。
WNOHANG //若由pid指定的子进程没有退出,则立即返回。waitpid不阻塞,返回值为0。
WUNTRACED //若某实现支持 作业控制,则有pid指定的任一子进程状态已暂停,其状态自暂停以来还没有报告过,则返回其状态。
- wait関数とwaitpid関数の違い
(1)子プロセスが終了する前に、waitは呼び出し元をブロックします。waitpidはブロックしません。
(2)waitはすべての子プロセスを待ち、waitpidは指定された子プロセスを待ちます。
8.プロセスグループ
- 1つ以上のプロセスのコレクション
- 同じ端末からさまざまな信号を受信できます。同じグループ内の共通の信号。
- 一意のプロセスグループID
- グループ内のすべてのプロセスが終了すると、プロセスグループは終了します。
- killコマンドは、プロセスグループにシグナルを送信します。
8.1プロセスグループ番号を取得する
前の記事では次のように述べています。
#include <unistd.h>
pid_t getpgrp(void); //获取当前进程所在的进程组ID标识。
pid_t getpgid(pid_t pid); //获取指定ID的进程所在的进程组ID标识。
8.2リーダープロセス
- プロセスグループの作成は、最初のプロセス(グループリーダープロセス)の参加から始まります。リーダープロセスのIDがグループIDとして使用されます。
- リーダープロセスは、プロセスグループとグループ内のプロセスを作成できます。
- ヘッドファイル
#include <unistd.h>
- 関数
(1) 将进程加入到指定的进程组中。成功返回0,错误返回-1。
int setpgid(pid_t pid,pid_t pgid);
(2) pid参数为指定的进程号,pgid为进程组。
8.3フォアグラウンドプロセスグループ
- Linuxでは、端末シグナルを自動的に受信するグループがフォアグラウンドプロセスグループになります。
- 端末では、CTRL + cなどの信号が最初にフォアグラウンドプロセスグループによって受信されます。
- シェルによって開始されたいくつかのプロセスグループ。デフォルトの親プロセスグループはフォアグラウンドプロセスグループです。
- ヘッドファイル
#include <unistd.h>
- 関数
(1)获取前台进程组ID。成功返回 前台进程组ID,错误返回-1。
pid_t tcgetpgrp(int fd);
(2)使用pgrpid,设置前台进程组ID
int tcsetpgrp(int fd,pid_t pgrpid);
(3)fd必须引用该会话的控制终端。0表示当前正在使用的终端。