1.首先是获取当前程序的pid和ppid(parent pid)
#include<stdio.h> #include<unistd.h> int main() { printf("the pid of this program is %d\n",(int)getpid()); printf("the parent pid is %d\n",(int)getppid()); return 0; }
执行过程中发现,多次执行后pid一般会变化,而ppid一般不会变,
2.在程序中创建新进程可以有两种方式,一种是直接通过system函数,该函数相当于创建一个子进程,并将函数内的参数传递给该子进程,等同于在命令行下执行该命令,若该shell无法执行,则返回值为127,其他错误则返回值-1,执行正确返回值0;
#include<stdlib.h> #include<stdio.h> int main() { int returnValue = system("ls -l"); printf("%d\n",returnValue); return 0; }
system函数执行的结果被返回到终端中输出
还有一种方法就是通过fork()/exec()创建新进程,fork()函数创建一个父进程的拷贝给子进程,并在调用fork函数处继续执行下去,创建出的子进程也继续在该出进行,fork函数在父进程中的返回值的子进程的pid,而在子进程中的返回值的0,以此可以用来分辨当前程序是出于子进程还是父进程中
#include<stdio.h> #include<unistd.h> //#include<sys/type.h> int main() { pid_t childPid = fork(); if(childPid != 0) { printf("now we are in parent progress,pid=%d\n",(int)getpid()); printf("now we are in parent progress,childPid = %d\n",childPid); } else { printf("now we are in child progress,pid = %d\n",(int)getpid()); printf("now we are in child progress,parentPid = %d\n",(int)getppid()); } return 0; }
执行结果:
now we are in child progress,pid = 19991 now we are in child progress,parentPid = 19990 now we are in parent progress,pid=19990 now we are in parent progress,childPid = 19991
3.首先介绍下exec函数族,总共有六个函数
#include <unistd.h> 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 execve(const char *path, char *const argv[], char *const envp[]);
这六个函数的第一个参数都是要执行的程序名,path代表需要详细的路径,file表示只需要传入程序名,然后会自动在执行路径中查找该程序名,const char *arg,...代表需要将参数依次传入到exec函数中,而char *const argv[] 代表以NULL结尾的字符串数组,如{"ls","-l",NULL};而envp代表另外的环境变量数组,且必须以{"variable=value",...,NULL}形式传入.如果exec函数执行正确,当前程序在exec执行后并不返回,否则要进行错误控制.代码如下:
#include<unistd.h> #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> int spawn(char *program,char **argv) { int childPid = fork(); if(childPid == -1) { fprintf(stderr,"error in fork:%s\n",strerror(errno)); return -1; } if(childPid != 0) { return childPid; } else { execvp(program,argv); fprintf(stderr,"error in exec function:%s\n",strerror(errno)); abort(); } } int main() { char *argList[]={"ls","-l","/root",NULL}; spawn("ls",argList); printf("done in main program\n"); return 0; }
但有一个问题就是,一般都是在main函数结束后才会输出exec的执行结果,因此可以通过在父进程中wait函数(需要添加#include<sys/wait.h>头文件)来等待子进程结束后再继续执行父进程代码.main函数如下所示,
int main() { int childStatus= 0; char *argList[]={"ls","-l","/root",NULL}; spawn("ls",argList); wait(&childStatus); if(WIFEXITED (childStatus)) { printf ("the child process exited normally, with exit code %d\n",WEXITSTATUS (childStatus)); } else { printf ("the child process exited abnormally\n"); } printf("done in main program\n"); return 0; }
还有waitpid函数后面跟特定的子进程pid,该函数可以用来等待特定的子进程结束后继续运行父进程,
5.当父进程中wait了子进程的结束,子进程结束后就会消失并将推出值通过wait函数返回到父进程中,若父进程没有wait子进程,则子进程的状态就丢失了,该子进程就变成了一个僵尸进程,
僵尸进程就是进程已经退出了,但父进程却并没有将子进程清理干净,因此可以在程序中定义一个sigaction函数,在响应函数中wait子进程结束.
4.nice命令能改变进程的nice值,nice值越大,优先级越高,一般的程序nice值为0,nice -n 10 sort input.txt > output.txt 命令对这个sort进程增加10的nice值,减少其优先级,renice命令可以直接更改nice值为指定值.只有root权限才能运用nice和renice更改nice值
5.signal函数
程序在运行过程中会收到各种各样的信号,如SIGTERM,SIGKILL,SIGINT,SIGUSR1,SIGUSR2等,附常用的signal信号:
SIGHUP 终止进程 终端线路挂断
SIGINT 终止进程 中断进程
SIGQUIT 建立CORE文件终止进程,并且生成core文件
SIGILL 建立CORE文件 非法指令
SIGTRAP 建立CORE文件 跟踪自陷
SIGBUS 建立CORE文件 总线错误
SIGSEGV 建立CORE文件 段非法错误
SIGFPE 建立CORE文件 浮点异常
SIGIOT 建立CORE文件 执行I/O自陷
SIGKILL 终止进程 杀死进程
SIGPIPE 终止进程 向一个没有读进程的管道写数据
SIGALARM 终止进程 计时器到时
SIGTERM 终止进程 软件终止信号
SIGSTOP 停止进程 非终端来的停止信号
SIGTSTP 停止进程 终端来的停止信号
SIGCONT 忽略信号 继续执行一个停止的进程
SIGURG 忽略信号 I/O紧急信号
SIGIO 忽略信号 描述符上可以进行I/O
SIGCHLD 忽略信号 当子进程停止或退出时通知父进程
SIGTTOU 停止进程 后台进程写终端
SIGTTIN 停止进程 后台进程读终端
SIGXGPU 终止进程 CPU时限超时
SIGXFSZ 终止进程 文件长度过长
SIGWINCH 忽略信号 窗口大小发生变化
SIGPROF 终止进程 统计分布图用计时器到时
SIGUSR1 终止进程 用户定义信号1
SIGUSR2 终止进程 用户定义信号2
SIGVTALRM 终止进程 虚拟计时器到时
Signal Value Action Comment
-------------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at tty
SIGTTIN 21,21,26 Stop tty input for background process
SIGTTOU 22,22,27 Stop tty output for background process
The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
程序在收到信号的时候会先停止当前运行的程序,然后转去执行相应的signal响应函数,执行完响应函数后在返回到原来执行处(有些signal执行后程序直接退出,就不会返回),
#include<signal.h> #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> sig_atomic_t count1 = 0;//sig_atomic_t是signal头文件中的宏定义,运算相比int更简便 sig_atomic_t count2 = 0; void siguser1Handle(int signalNumber)//当收到SIGUSR1信号后的相应函数 { printf("receive signal SIGUSER1\n"); fflush(stdout);//stdout有缓冲区,只有fflush后才会即时输出stdout中的字符串 count1++; } void siguser2Handle(int signalNumber) { printf("receive signal SIGUSER2\n"); fflush(stdout); count2++; } int main() { /*struct sigaction sa; memset(&sa,0,sizeof(sa)); sa.sa_handler = &siguser1Handle;//sigaction中的sa_handle有三种值,第一种是是SIG_DFL,代表使用该信号默认的响应函数,第二种是SIG_IGN,代表忽略该信号,第三种就是指向自定义的信号响应函数. sigaction (SIGUSR1, &sa, NULL); struct sigaction sb; memset(&sb,0,sizeof(sb)); sb.sa_handler = &siguser2Handle; sigaction (SIGUSR2, &sb, NULL);*/ if(signal(SIGUSR1,&siguser1Handle) == SIG_ERR)//接受SIGUSR1信号并相应 fprintf(stderr,"error in receive SIGUSR1 signal");//stderr没有缓冲区,立马就能输出 if(signal(SIGUSR2,&siguser2Handle) == SIG_ERR)//接受SIGUSR2信号并相应 fprintf(stderr,"error in receive SIGUSR2 signal");//这四行和main函数内被注释掉代码的是一样的结果,第一种更严谨些 for(int i = 0;i < 3;i++) { sleep(10); } printf("receive %d times SIGUSER1\n",count1); printf("receive %d times SIGUSER2\n",count2); return 0;
在程序为可以通过指令kill -USR1 pid为pid对应的进程发送SIGUSR1信号,
程序中可以通过kill函数为某个进程发送signal信号,如kill(pid,SIGUSR1),需要包含sys/types.h和signal.h两个头文件.
程序退出如果是用exit(number)的话,函数内的number参数应当是0-127范围,因为如果通过signal来杀死进程的话,返回值是128+signal值
下面是给线程发送signal并执行signal函数的过程,步骤是先在主线程中定义好signal函数,然后给分线程发送signal.
#include<stdio.h> #include<signal.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<string.h> sig_atomic_t count=0; void siguser1Handle(int signalNumber) { count++; printf("receive %d times SIGUSR1\n",count); fflush(stdout); } void *thread_func(void *args) { while(1) { printf("thread \n"); fflush(stdout); sleep(5); } } int main() { pthread_t thread_id; struct sigaction sb; memset(&sb,0,sizeof(sb)); sb.sa_handler = &siguser1Handle; sigaction (SIGUSR1, &sb, NULL);//在主线程中定义好signal函数,然后在主线程中给分线程发送signal并执行signal handlers函数. pthread_create(&thread_id,NULL,thread_func,NULL); for(int i = 0;i < 3;i++) { pthread_kill(thread_id,SIGUSR1); sleep(3); } pthread_join(thread_id,NULL); pause(); return 0; }