以下内容源于网络资源的学习与整理,如有侵权请告知删除。
一、fork()函数:父进程创建子进程
函数原型
所需头文件 #include <unistd.h>
#include <sys/types.h>
函数原型 pid_t fork(void); 返回值 在子进程中返回0,在父进程中返回子进程的PID,失败则返回一个负值。 函数作用
fork()函数用于从已经存在的进程中创建一个新进程。新进程叫做子进程,原进程叫做父进程。子进程是父进程的一个复制品,它从父进程继承整个进程的地址空间(每个进程的地址空间是独立的),包括进程上下文、代码段、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等等,子进程所独有的只有其进程号、资源使用和计时器等等。
补充说明
1、使用fork()的典型方法
使用fork()后用 if 判断返回值,如果返回值大于0时就是在父进程中,等于0就是在子进程中,然后在各自的{ }里做操作。
2、fork()创建进程的方式
操作系统利用已有的进程,来复制生成一个新的进程。因为重新创建进程的代价很大。
3、父进程与子进程的关系
子进程一旦建立,子进程不再依赖于父进程。子进程有自己独立的PCB,被内核同等调度。
4、fork()的返回值情况
fork函数调用一次,会返回两次,因为父子都有相同的一份代码,进程同时被运行。fork函数在父进程中返回子进程的PID;在子进程中返回0。
代码示例
p1 =fork();if(p1=0) //子进程 {printf("hello word,pid = %d \n",getpid()); }if(p1>0) //父进程 {printf("hello word,pid = %d \n",getpid()); }
二、exec函数族:在进程中启动另一个程序
函数原型
所需头文件 #include <unistd.h>
函数原型 int execl(const char *path, const char *arg, ...); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg,..., char * const envp[]); int execvpe(const char *file, char *const argv[],char *const envp[]); 函数作用
execl函数族用于在进程中启动另一个可执行文件,可以根据指定的文件名或者目录名找到可执行文件,并用它来替代调用这个函数的进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号,其他内容均被新进程替换。
补充说明
1、为什么需要excel函数族?
fork()创建子进程后,子进程可以独立执行一个新的程序,比如可以在子进程 if(){ } 中写入新程序的代码。但是这样操作不够灵活,因为它需要我们键入源代码,这意味着我们必须知道源代码。对于一些没有源代码而只有编译后的可执行程序(比如 ls -la 命令),就不能使用这种方式。而使用exec族函数,可以直接把一个编译好的可执行程序直接加载运行。
2、excel函数族的应用场景
(1)进程可以调用excel函数族中任意一个函数让自己重生。
(2)如果想进行多进程运行,则父进程先调用fork()函数创建一个子进程,在子进程里使用execl函数族中的任意一个函数来执行一个可执行程序。
代码示例
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h>int main(void) {pid_t pid = -1;pid_t ret = -1;int status = -1;pid = fork();if (pid > 0) // 父进程{printf("parent, 子进程id = %d.\n", pid);}else if (pid == 0) // 子进程{//execl("/bin/ls", "ls", "-l", "-a", NULL); // ls -l -a//char * const arg[] = {"ls", "-l", "-a", NULL};//execv("/bin/ls", arg);//execl("hello", "aaa", "bbb", NULL);//char * const arg[] = {"aaa", "bbb", NULL};//execv("hello", arg);//execlp("ls", "ls", "-l", "-a", NULL); char * const envp[] = {"AA=aaaa", "XX=abcd", NULL};execle("hello", "hello", "-l", "-a", NULL, envp);return 0;}else{perror("fork");return -1;}return 0; }
三、exit()函数:终止进程
函数原型
所需头文件 #include <stdlib.h>
函数原型 void exit(int status); 返回值 status是一个整型参数,利用这个参数可以传递进程结束时的状态。一般来说,0表示正常结束,其他数字表示出现了错误,进程非正常结束。在实际编码中,可以利用wait()函数接收子进程的返回值,从而针对不同的情况进行不同的处理。 函数作用
exit()函数用来终止进程。当调用此函数时,进程会无条件地停止剩余操作,终止本进程的运行。
补充说明
无。
四、wait()函数:父进程回收子进程
函数原型
所需头文件 #include <sys/wait.h>
#include <sys/types.h>
函数原型 pid_t wait(int *status); 参数说明 这里的status是一个整型指针,是该子进程退出时的状态。若status不为空,则通过它可以获得子进程的结束状态。另外,子进程的结束状态可由Linux中的一些特定的宏来检测。 返回值 成功返回已经结束的子进程的的进程号。失败则返回-1。 函数作用
wait()函数主要用来回收子进程的资源,同时获取被回收的子进程的进程号与退出状态。
补充说明
(1)wait()函数的返回值
成功则返回本次被回收的子进程的进程号。当前进程可能有多个子进程,wait()函数的返回值可以用来判断哪一个子进程被回收了。
(2)wait()函数的工作原理
父进程调用wait函数后会进入阻塞。当子进程结束时,系统向其父进程发送SIGCHILD信号,父进程被SIGCHILD信号唤醒后,会去回收僵尸子进程。若父进程没有任何子进程,或者子进程已经结束,则wait立即返回。父子进程之间是异步的,SIGCHILD信号机制就是为了解决父子进程之间的异步通信问题,让父进程及时地回收僵尸子进程。
(3)检测子进程退出状态的宏
- WIFEXITED:用来判断子进程是否正常终止(return、exit、_exit退出);
- WIFSIGNALED:用来判断子进程是否非正常终止(被信号所终止);
- WEXITSTATUS:用来得到正常终止情况下的进程返回值。
代码示例
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h>int main(void) {pid_t pid = -1;pid_t ret = -1;int status = -1;pid = fork();if (pid > 0){// 父进程//sleep(1);printf("parent.\n");ret = wait(&status);printf("子进程已经被回收,子进程pid = %d.\n", ret);printf("子进程是否正常退出:%d\n", WIFEXITED(status));printf("子进程是否非正常退出:%d\n", WIFSIGNALED(status));printf("正常终止的终止值是:%d.\n", WEXITSTATUS(status));}else if (pid == 0){// 子进程printf("child pid = %d.\n", getpid());return 51;//exit(0);}else{perror("fork");return -1;}return 0; }
五、getpid()函数:获取当前进程的进程号
函数原型
所需头文件 #include <sys/types.h>
#include <unistd.h>
函数原型 pid_t getpid(void);//获取当前进程的进程号
(pid_t getppid(void);//获取当前进程的父进程的进程号)
返回值 返回值表示当前进程的进程号。 函数作用
getpid()函数用来获取当前进程的进程号。
补充说明
在shell命令行中执行某个程序,程序中获取当前进程的父进程的进程号,会发现脚本进程bash就是当前进程的父进程。
xjh@ubuntu:~/iot/tmp$ ./a.out pid = 4963 parent id = 3009 //获取当前进程的父进程的进程号,得到3009 xjh@ubuntu:~/iot/tmp$ psPID TTY TIME CMD3009 pts/0 00:00:01 bash4964 pts/0 00:00:00 ps //bash这个脚本进程的进程号,是3009 xjh@ubuntu:~/iot/tmp$ cat test.c #include<stdio.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char **argv) {pid_t p1=-1,p2=-2;p1=getpid();p2=getppid();printf("pid = %d \n", p1);printf("parent id = %d \n",p2);return 0; } xjh@ubuntu:~/iot/tmp$
六、system()函数:执行某命令
函数原型
所需头文件 #include <stdlib.h> 函数原型 int system(const char *command); 返回值 命令执行成功返回0,执行失败返回-1。 函数作用
执行 Shell 命令,其中command为命令名。
补充说明
system函数会调用fork函数产生子进程,由子进程来执行command命令,命令执行完后随即返回原调用的进程。也就是说,system()函数等价于fork()函数+execl族其中一个函数。
代码示例
#include <stdio.h> #include<string.h> #include<stdlib.h> int main(void) {char input[100];system("shutdown -h 1 &");//这是linux系统下的一个关机命令printf("你的电脑将在一分钟后关机,如果输入“我是猪”,就会取消关机!\n请输入: ");scanf("%s",input);while(strcmp(input,"我是猪")){printf("输入错误,请输入“我是猪”来取消关机!\n请输入: ");scanf("%s",input);}system("shutdown -c");//取消关机命令return 0; }