소개 | Linux에서는 CPU 시간만 기다리는 프로세스를 준비 프로세스라고 하며 실행 대기열에 배치되며 준비 프로세스의 상태 플래그는 TASK_RUNNING입니다. 실행 중인 프로세스의 시간 조각이 소진되면 Linux 커널 스케줄러는 프로세스의 CPU 제어를 박탈하고 실행 대기열에서 실행할 적절한 프로세스를 선택합니다. |
물론 프로세스는 CPU 제어를 적극적으로 해제할 수도 있습니다. Schedule() 함수는 다른 프로세스가 CPU를 점유하도록 예약하기 위해 프로세스에서 적극적으로 호출할 수 있는 예약 함수입니다. CPU를 적극적으로 포기한 프로세스가 CPU를 점유하도록 다시 예약되면 마지막으로 실행을 중지한 위치에서 실행이 시작됩니다. 즉, Schedule()을 호출하는 다음 코드 줄부터 실행이 시작됩니다.
때때로 프로세스는 장치 초기화, I/O 작업 완료 또는 타이머 만료와 같은 특정 이벤트가 발생할 때까지 기다려야 합니다. 이 경우 프로세스는 실행 큐에서 제거되고 대기 큐에 추가되어야 합니다. 이때 프로세스는 슬립 상태에 진입합니다.
하나는 중단 가능한 절전 상태이고 상태 플래그는 TASK_INTERRUPTIBLE
입니다.
다른 하나는 중단할 수 없는 절전 상태이며 상태 플래그는 TASK_UNINTERRUPTIBLE입니다. 인터럽트 가능한 절전 상태의 프로세스는 특정 조건이 충족될 때까지 절전 모드로 유지됩니다. 예를 들어 하드웨어 인터럽트 발생, 프로세스가 기다리고 있는 시스템 리소스 해제, 신호 전달 등이 프로세스를 깨우는 조건이 될 수 있습니다. 무정전 슬립 상태는 인터럽트 가능 슬립 상태와 유사하지만 한 가지 예외가 있다. 즉, 이 슬립 상태에 신호를 전달하는 프로세스는 상태를 변경할 수 없다. 즉, 깨우라는 신호에 응답하지 않는다는 것이다. 중단 불가능한 절전 상태는 일반적으로 덜 사용되지만 일부 특정 상황에서는 여전히 매우 유용합니다. 예를 들어 프로세스는 특정 이벤트가 발생할 때까지 기다려야 하며 중단될 수 없습니다.
최신 Linux 운영 체제에서는 일반적으로 프로세스가 Schedule()을 호출하여 절전 상태로 전환됩니다.
실행 중인 프로세스를 절전 모드로 전환하는 방법을 보여줍니다.
Sleeping_task = 현재;
set_current_state(TASK_INTERRUPTIBLE);
일정();
func1();
/* 나머지 코드 ... */
첫 번째 명령문에서 프로그램은 프로세스 구조 포인터 Sleeping_task를 저장하고 current는 현재 실행을 가리키는 매크로입니다
프로세스 구조. set_current_state()는 프로세스 상태를 실행 상태 TASK_RUNNING에서 절전 상태로 변경합니다
TASK_INTERRUPTIBLE. TASK_RUNNING 상태의 프로세스에 의해 스케줄()이 예약된 경우, 스케줄()은 CPU를 점유하도록 다른 프로세스를 예약합니다. TASK_INTERRUPTIBLE 또는 TASK_UNINTERRUPTIBLE 상태의 프로세스에 의해 스케줄()이 예약된 경우 추가 단계가 있습니다. 실행 중: 현재 실행 중인 프로세스는 다른 프로세스가 예약되기 전에 실행 큐에서 제거됩니다. 이로 인해 실행 중인 프로세스가 더 이상 실행 큐에 없기 때문에 절전 모드로 전환됩니다.
방금 잠들었던 프로세스를 깨우기 위해 다음 함수를 사용할 수 있습니다.
wake_up_process(sleeping_task);
wake_up_process()를 호출한 후 잠자는 프로세스의 상태는 TASK_RUNNING으로 설정되고 스케줄러
실행 대기열에 추가됩니다. 물론 이 프로세스는 다음에 스케줄러가 예약한 경우에만 실행될 수 있습니다.
잘못된 깨우기
거의 모든 경우 특정 조건을 확인하고 조건이 충족되지 않으면 프로세스가 절전 모드로 전환됩니다. 하지만 가끔
그러나 판단 조건이 참이면 프로세스는 계속 Sleep 상태가 됩니다. 이것이 바로 Invalid Wake-up 문제입니다. 운영 체제에서 여러 프로세스가 공유 데이터에 대해 일부 처리를 시도하고 최종 결과가 프로세스가 실행되는 순서에 따라 달라지는 경우 이는 운영 체제의 일반적인 문제입니다. up 그것은 바로 경쟁적 상황 때문이다.
두 개의 프로세스 A와 B가 있다고 가정해 보겠습니다. 프로세스 A는 연결된 목록이 비어 있지 않은지 확인해야 합니다.
테이블의 데이터는 일부 작업을 진행 중이며 프로세스 B도 연결 목록에 노드를 추가하고 있습니다. 연결리스트가 비어 있으면 연산할 데이터가 없기 때문에 프로세스 A는 휴면 상태가 되고, 프로세스 B가 연결 목록에 노드를 추가하면 프로세스 A를 깨웁니다. 코드는 다음과 같습니다.
프로세스:
1 spin_lock(&list_lock);
2 if(list_empty(&list_head)) {
3 spin_unlock(&list_lock);
4 set_current_state(TASK_INTERRUPTIBLE);
5일정();
6 spin_lock(&list_lock);
7}
8
9 /* 나머지 코드 ... */
10 spin_unlock(&list_lock);
B 프로세스:
100 spin_lock(&list_lock);
101 list_add_tail(&list_head, new_node);
102 spin_unlock(&list_lock);
103 wake_up_process(processa_task);
여기에 문제가 있습니다. 프로세스 A가 라인 3 이후와 라인 4 이전에 실행되면 프로세스 B가 다른 프로세서에 의해 예약됩니다
서비스를 시작합니다. 이 시간 조각 내에서 프로세스 B는 모든 명령을 완료하여 프로세스 A를 깨우려고 시도합니다. 이때 프로세스 A는 아직 절전 모드에 진입하지 않았으므로 깨우기 작업이 유효하지 않습니다. 그 후에도 프로세스 A는 계속 실행됩니다. 이때 연결된 목록이 아직 비어 있다고 잘못 생각하여 상태를 TASK_INTERRUPTIBLE로 설정한 다음 Schedule()을 호출하여 절전 모드로 전환합니다. 프로세스 B가 wake-up 호출을 놓쳤기 때문에 무한정 sleep 상태가 됩니다. 이는 연결 리스트에 처리해야 할 데이터가 있어도 프로세스 A가 여전히 sleep 상태이기 때문에 잘못된 wake-up 문제입니다.
상태 이전에는 프로세스 B의 wake_up_process()가 원래 프로세스 A의 상태를 TASK_RUNNING으로 설정할 수 있는 기회를 제공했지만, 아쉽게도 이때 프로세스 A의 상태는 여전히 TASK_RUNNING이므로 wake_up_process()는 프로세스 A의 상태를 sleep에서 변경했습니다. 상태에서 실행 중 상태로의 노력이 원하는 효과를 얻지 못했습니다. 이 문제를 해결하려면 연결된 목록이 비어 있는지 확인하고 프로세스 상태를 절전 상태로 설정하는 것이 필수 단계가 되도록 보장 메커니즘을 사용해야 합니다. 즉, wake_up_process가 발생하도록 경쟁 조건의 원인을 제거해야 합니다. ( )는 잠들어 있는 프로세스를 깨우는 역할을 할 수 있다.
이유를 찾은 후 위 예에서 잘못된 깨우기 문제를 방지하기 위해 프로세스 A의 코드 구조를 다시 설계합니다.
프로세스:
1 세트_현재_상태(TASK_INTERRUPTIBLE);
2 spin_lock(&list_lock);
3 if(list_empty(&list_head)) {
4 spin_unlock(&list_lock);
5일정();
6 spin_lock(&list_lock);
7}
8 set_current_state(TASK_RUNNING);
9
10 /* 나머지 코드 ... */
11 spin_unlock(&list_lock);
보시다시피 이 코드는 조건을 테스트하기 전에 현재 실행 프로세스 상태를 TASK_INTERRUPTIBLE로 설정하고 연결된 목록이 비어 있지 않으면 TASK_RUNNING 상태로 설정합니다. 이런식으로 B프로세스가 A프로세스에 있다면 프로세스를 확인하세요
연결된 목록이 비어 있는 후 wake_up_process()를 호출하면 프로세스 A의 상태가 원래 TASK_INTERRUPTIBLE
에서 자동으로 변경됩니다.
이후에는 TASK_RUNNING이 된다. 다시 스케줄()을 호출하더라도 현재 상태가 TASK_RUNNING이므로 여전히 런큐에서 제거되지 않으므로 실수로 슬립에 들어가는 일도 없을 것이고, 당연히 문제가 된다. 유효하지 않은 깨우기가 방지됩니다.
Linux 커널 예
Linux 운영 체제에서는 Linux 운영 체제 커널의 잘못된 깨우기 문제를 방지하려면 커널의 안정성이 중요합니다.
Linux 커널은 프로세스를 절전 모드로 전환해야 할 때 다음과 유사한 작업을 사용해야 합니다.
/* 'q'는 우리가 잠들고 싶은 대기 큐입니다 */
DECLARE_WAITQUEUE(대기,현재);
add_wait_queue(q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
/* 또는 TASK_INTERRUPTIBLE */
while(!condition) /* 'condition'은 대기 조건입니다*/
일정();
set_current_state(TASK_RUNNING);
Remove_wait_queue(q, &wait);
위 작업을 통해 프로세스는 다음 일련의 단계를 통해 절전 대기 대기열에 안전하게 추가될 수 있습니다. 먼저 조정
DECLARE_WAITQUEUE()를 사용하여 대기 대기열 항목을 만든 다음 add_wait_queue()를 호출하여 대기 대기열에 자신을 추가하고 프로세스 상태를 TASK_INTERRUPTIBLE 또는 TASK_INTERRUPTIBLE로 설정합니다. 그런 다음 루프는 조건이 참인지 확인합니다. 조건이 참이면 잠을 잘 필요가 없으며, 조건이 참이 아니면 Schedule()이 호출됩니다. 프로세스가 확인한 조건이 충족되면 프로세스는 자신을 TASK_RUNNING으로 설정하고 Remove_wait_queue()를 호출하여 대기 대기열에서 자신을 제거합니다.
위에서 볼 수 있듯이 Linux 커널 코드 유지관리자는 프로세스가 조건을 확인하기 전에 프로세스 상태를 절전 상태로 설정합니다.
그런 다음 루프는 조건을 확인합니다. 프로세스가 절전 모드를 시작하기 전에 조건이 충족되면 루프가 종료되고 set_current_state()를 사용하여 상태를 준비 상태로 설정합니다. 이는 또한 프로세스가 실수로 절전 모드로 들어가는 경향이 없도록 보장합니다. 잘못된 깨우기 문제는 발생하지 않습니다.
Linux 커널의 예를 사용하여 Linux 커널이 잘못된 절전 모드를 방지하는 방법을 살펴보겠습니다. 이 코드는 Linux2.6 커널(linux-2.6.11/kernel/sched.c: 4254)에서 제공됩니다.
4253 /* kthread_stop을 기다립니다 */
4254 set_current_state(TASK_INTERRUPTIBLE);
4255 동안 (!kthread_should_stop()) {
4256 일정();
4257 set_current_state(TASK_INTERRUPTIBLE);
4258 }
4259 __set_current_state(작업 실행 중);
4260은 0을 반환합니다;
위 코드는 마이그레이션 서비스 스레드 migration_thread에 속합니다. 이 스레드는 kthread_should_stop(),
을 지속적으로 확인합니다.
kthread_should_stop()이 1을 반환할 때까지 루프를 종료할 수 없습니다. 이는 kthread_should_stop()이 0을 반환하는 한 프로세스가 절전 모드 상태라는 의미입니다. 프로세스 상태가 TASK_INTERRUPTIBLE로 설정된 후에 kthread_should_stop() 검사가 실제로 실행되는 것을 코드에서 볼 수 있습니다. 따라서 조건 확인 후, Schedule() 이전에 다른 프로세스가 깨우려고 시도하더라도 해당 프로세스의 깨우기 작업은 무효화되지 않습니다.
상태는 TASK_INTERRUPTIBLE 또는 TASK_UNINTERRUPTIBLE로 설정되며, 확인된 조건이 충족되면
상태를 TASK_RUNNING으로 재설정합니다. 이렇게 하면 프로세스의 대기 조건이 충족되었는지 여부에 관계없이 프로세스가 준비 큐에서 제거되므로 실수로 슬립 상태에 진입하지 않으므로 유효하지 않은 깨우기 문제를 피할 수 있습니다.
위 내용은 마스터는 Linux 프로세스의 절전 모드 및 절전 모드 해제 설정 방법을 가르칩니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!