Продемонстрируем действие функций этого семейства на примере программы father.c:
Программа father.c:
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
char * const args[]={
"./son.out",
NULL};
main(int argc, char *argv[]){
int status;
switch(argv[1][0]){
case '1':
spawnl(P_WAIT,"./son.out","son", NULL);
printf("father still live!\n");
break;
case '2':
spawnle(P_NOWAIT,"./son.out","son",NULL,NULL);
printf("father still live!\n");
break;
case '3':
spawnlp(P_NOWAITO,"./son.out","son",NULL);
printf("father still live!\n");
break;
case '4':
spawnv(P_OVERLAY,"./son.out",args);
printf("father still live!\n");
break;
case '5':
spawnvp(P_WAIT,"./son.out",args);
printf("father still live!\n");
break;
}
wait(&status);
printf("father is dying\n");
}
Программа son.c:
#include <stdio.h>
main(){
sleep(5);
printf("I'm son\n");
}
Результат работы:
$ cc -o father.out father.c
$ cc -o son.out son.c
$ ./father.out 1
I'm son
father still live!
father is dying
$ ./father.out 2
father still live!
I'm son
father is dying
$ ./father.out 3
father still live!
father is dying
$ I'm son
$ ./father.out 4
I'm son
$ ./father.out 5
I'm son
father still live!
father is dying
Если провести аналогии с POSIX-функциями, то spawn-семейство заменяет конструкцию fork() + exec[l,e,v,p](), расширенную дополнительными режимами. Так мы наблюдаем приостановку выполнения father на время работы son в режиме P_WAIT, что соответствует введению в программу функции wait(). Режимы P_NOWAIT и P_NOWAITO аналогичны паре fork() + exec(), различие между ними покажем дальше. P_OVERLAY – просто exec().
Для иллюстрации методов решения проблемы zombie используем набор программ. Во-первых, father2.c и son2.c, чтобы продемонстрировать факт появления zombie.
Программа father2.c:
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
main(int argc, char *argv[]){
int status;
//signal(SIGCHLD,SIG_IGN);
spawnl(P_NOWAIT,argv[1],argv[1],NULL);
printf("father still live!\n");
while(1){
sleep(1);
}
}
Программа son2c:
#include <stdio.h>
#include <process.h>
main(){
sleep(5);
printf("I'm son, my PID is %i\n",getpid());
}
Первый способ: игнорировать сигнал SIGCHLD, посылаемый потомком:
Программа father3.c:
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
main(int argc, char *argv[]){
int status;
signal(SIGCHLD,SIG_IGN);
spawnl(P_NOWAIT,argv[1],argv[1],NULL);
printf("father still live!\n");
while(1){
sleep(1);
}
}
Программа son3.c:
#include <stdio.h>
#include <process.h>
#include <signal.h>
main(){
sleep(5);
printf("I'm son, my PID is %i\n",getpid());
}
Второй способ: использование при обращении к spawn() режима P_NOWAITO, который подразумевает наличие признака _SPAWN_NOZOMBIE:
Программа father4.c:
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[]){
int status;
spawnl(P_NOWAITO,argv[1],argv[1],NULL);
printf("father still live!\n");
while(1){
sleep(1);
}
}
В результате можно наблюдать следующее:
$ cc -o father2.out father2.c
$ cc -o son2.out son2.c
$ ./father2.out
$ ./father2.out 2
father still live!
I'm son, my PID is 1114152
$ pidin -p 1114152
pid tid name prio STATE Blocked
1114152 (Zombie)
$ cc -o son3.out son3.c
$ cc -o father3.out father3.c
$ ./father3.out ./son3.out
father still live!
I'm son, my PID is 1253416
$ pidin -p 1253416
pid tid name prio STATE Blocked
pidin: couldn't open /proc/1253416/as: No such file or directory
$ cc -o son3.out son3.c
$ cc -o father4.out father4.c
$ ./father4.out ./son3.out
father still live!
I'm son, my PID is 1298472
$ pidin -p 1298472
pid tid name prio STATE Blocked
pidin: couldn't open /proc/1298472/as: No such file or directory
7. Управление приоритетами
Помимо описанного выше способа управления приоритетами при помощи утилиты nice, эту проблему можно решить с помощью функций библиотеки sched.
int getpriority(pid_t pid)
int setpriority(pid_t pid, int prio)
где pid – PID процесса, операцию над которым требуется совершить
prio – новое значение его приоритета
Рассмотрим использование этих функций на примере программы getp.c, которая позволяет получить сведения о текущем значении приоритета процесса (в нашем случае – программма test_proc.c) по PID и изменить его.
Программа getp.c:
#include <stdio.h>
#include <sched.h>
#include <stdio.h>
main(int argc, char* argv[]){
int j, i=0, pid=0;
if(argc<2)
exit(-1);
while(argv[1][i]){
i++;
}
for(j=0;j<i;j++){
pid=pid*10+argv[1][j]-0x30;
}
j=getprio(pid);
printf("Priority of PID %i is equal to %i\nEnter increment: ",pid,j);
scanf("%i",&i);
setprio(pid,j+i);
printf("Now priority of PID %i is equal to %i\n",pid,j=getprio(pid));
}
Программа test_proc.c:
#include <stdio.h>
#include <process.h>
main(){
printf("test_proc PID is %i\n",getpid());
while(1){
sleep(1);
}
}
Результат работы:
$ ./test_proc.out&
$ cc -o getp.out getp.c
$ ./getp.out 1548325
Priority of PID 1548325 is equal to 10
Enter increment: 5
Now priority of PID 1548325 is equal to 15
$ ps -l -p 1548325
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
00000200 - 100 1548325 1355809 - 15 0 - 568K - ? 00:00:00 ./test_proc.out
Также возможно использовать семейство функций, предназначенных для работы с параметрами процесса, относящимися к задаче планирования, оформленными в структуру sched_param: sched_getparam() и sched_setparam().
Программа getp2.c:
#include <stdio.h>
#include <sched.h>
#include <stdio.h>
main(int argc, char* argv[]){
struct sched_param str;
int j, i=0, pid=0;
if(argc<2)
exit(-1);
while(argv[1][i]){
i++;
}
for(j=0;j<i;j++){
pid=pid*10+argv[1][j]-0x30;
}
sched_getparam(pid, &str);
printf("Priority of PID %i is equal to %i\nEnter increment: ",pid,str.sched_priority);
scanf("%i",&i);
str.sched_priority+=i;
sched_setparam(pid,&str);
sched_getparam(pid,&str);
printf("Now priority of PID %i is equal to %i\n",pid,str.sched_priority);
}
Результат работы:
$ cc -o getp2.out getp2.c
$ ./getp2.out 1810469
Priority of PID 1810469 is equal to 15
Enter increment: 1
Now priority of PID 1810469 is equal to 16
$ ps -l -p 1810469
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
00000200 - 100 1810469 1355809 - 16 0 - 568K - ? 00:00:00 ./test_proc.out
Также существуют инструменты контроля и изменения самой политики планирования. К ним относятся функции получения сведений о предельных значениях приоритета при разных политиках: sched_get_priority_min(), sched_get_priority_max().
Программа getp3.c:
#include <stdio.h>
#include <sched.h>
#include <stdio.h>
main(int argc, char* argv[]){
printf("Policies MinPrio MaxPrio\n");
printf("FIFO %12i%12i\n",sched_get_priority_min(SCHED_FIFO),sched_get_priority_max(SCHED_FIFO));
printf("RR %12i%12i\n",sched_get_priority_min(SCHED_RR),sched_get_priority_max(SCHED_RR));
printf("OTHER %12i%12i\n",sched_get_priority_min(SCHED_OTHER),sched_get_priority_max(SCHED_OTHER));
printf("SPORADIC%12i%12i\n",sched_get_priority_min(SCHED_SPORADIC),sched_get_priority_max(SCHED_SPORADIC));
}
Результат ее работы:
$ cc -o getp3.out getp3.c
$ ./getp3.out
Policies MinPrio MaxPrio
FIFO 1 63
RR 1 63
OTHER 1 63
SPORADIC 1 63
8. Приоритетность родственных процессов
Когда речь заходит о планировании, возникает вопрос, какой из процессов назначать на выполнении при равенстве их приоритетов. Рассмотрим пример того, как эта проблема решается при одновременной работе нескольких процессов:
Программа forks.c:
#include <sys/types.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
main(){
if(!fork()){
if(!fork()){
if(!fork()){
printf("I'm process #4, my PID is %i, PPID is %i\n", getpid(),getppid());
exit(0);
}
else{
printf("I'm process #3, my PID is %i, PPID is %i\n", getpid(),getppid());
exit(0);
}
}
else{
printf("I'm process #2, my PID is %i, PPID is %i\n", getpid(),getppid());
exit(0);
}
}
else{
printf("I'm process #1, my PID is %i, PPID is %i\n", getpid(),getppid());
exit(0);
}
}
Результат выполнения:
$ ./forks.out
I'm process #2, my PID is 659492, PPID is 655395
I'm process #3, my PID is 659493, PPID is 659492
I'm process #4, my PID is 659494, PPID is 659493
I'm process #1, my PID is 655395, PPID is 532514
Как можно видеть, предпочтение отдается наиболее старшему процессу.
9. Выводы
При выполнении данной работы я ознакомился с организацией многозадачности в ОС QNX на примере различных средств контроля и управления. Можно выделить следующие основные классы рассмотренных задач:
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.