1.创建进程fork函数
创建进程一般目的
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 int main()
5 {
6 pid_t pid;
7 int data;
8 while(1)
9 {
10 printf("please input data\n");
11 scanf("%d",&data);
12 if(data==1)
13 {
14 pid=fork();
15 if(pid>0)//父进程继续等待输入
16 {
17
18 }else if(pid==0)//子进程
19 {
20 while(1){
21 printf("do net request,pid %d\n",getpid());
22 sleep(3);
23 }
24 }
25
26 }else
27 {
28 printf("wait input data\n");
29 }
30
31
32 }
33
34 return 0;
35 }
2.创建进程vfork函数
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);//原型
vfork函数 也可以创建进程,与fork有什么区别
关键区别一:
vfork 直接使用父进程存储空间,不拷贝。
关键区别二:
vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t pid;
8 int cnt;
9 printf("pid %d\n",getpid());
10 pid=vfork();
11
12 if(pid>0)
13 {
14 while(1){
15 printf("This is father Pid %d pid %d\n",getpid(),pid);
16 sleep(1);
17 printf("cnt %d\n",cnt);//父子进程共享存储空间,cnt会被子进程改变
18 }
19 }
20 else if(pid==0){
21 while(1){
22 printf("this is son pid %d, pid %d\n",getpid(),pid);
23 sleep(1);
24 cnt++;
25 if(cnt>=3)//子进程运行3次退出
26 {
27
28 exit(0);
29 }
30 }
31 }
32
33
34 return 0;
35 }
3.进程退出
1.正常退出
Main函数调用return
进程调用exit(),标准c库
进程调用_exit()或者_Exit(),属于系统调用
补充:
进程最后一个线程返回
最后一个线程调用pthread_exit
2.异常退出
调用abort
当进程收到某些信号时,如ctrl+C
最后一个线程对取消(cancellation)请求做出响应
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>v
void _Exit(int status);
4.父进程等待子进程退出
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
父进程等待子进程退出,并收集子进程的退出状态
子进程退出状态不被收集,变成僵死进程(僵尸进程)
wait与waitpid区别:waip使调用者阻塞,waitpid有一个选项,可以使调用者不阻塞
wait
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include<stdlib.h>
5 #include <sys/wait.h>
6
7 int main()
8 {
9 pid_t pid;
10 int cnt=0;
11 int staues=10;
12 printf("F pid %d\n",getpid());
13 pid=fork();
14
15 if(pid>0)
16 {//若不wait,子进程退出会变成僵尸进程
17 wait(&staues);//等待子进程退出,不关心子进程退出状态可写NULL
18 printf("this staues=%d\n",WEXITSTATUS(staues));//WEXITSTATUS宏解析子进程退出码
19 while(1){
20 printf("This is father Pid %d pid %d\n",getpid(),pid);
21 sleep(1);
22 printf("cnt %d\n",cnt);
23 }
24 }
25 else if(pid==0){
26 while(1){
27 printf("this is son pid %d, pid %d\n",getpid(),pid);
28 sleep(1);
29 cnt++;
30 if(cnt>=5)
31 {
32
33 exit(3);
34 }
35 }
36 }
37
38
39 return 0;
40 }
waitpid
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include<stdlib.h>
5 #include <sys/wait.h>
6
7 int main()
8 {
9 pid_t pid;
10 int cnt=0;
11 int staues=10;
12 printf("F pid %d\n",getpid());
13 pid=fork();
14
15 if(pid>0)
16 {
17 waitpid(pid,&staues,WNOHANG);//此时pid为子进程pid,WHOHANG为不阻塞模式
18 printf("this staues=%d\n",WEXITSTATUS(staues));
19 while(1){
20 printf("This is father Pid %d pid %d\n",getpid(),pid);
21 sleep(1);
22 printf("cnt %d\n",cnt);
23 }
24 }
25 else if(pid==0){
26 while(1){
27 printf("this is son pid %d, pid %d\n",getpid(),pid);
28 sleep(1);
29 cnt++;
30 if(cnt>=5)
31 {
32
33 exit(3);//退出后子进程为僵尸进程
34 }
35 }
36 }
37
38
39 return 0;
40 }
孤儿进程
父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程
Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include<stdlib.h>
5 #include <sys/wait.h>
6
7 int main()
8 {
9 pid_t pid;
10 int cnt=0;
11 int staues=10;
12 printf("F pid %d\n",getpid());
13 pid=fork();
14
15 if(pid>0)
16 {
17 printf("this is father pid %d\n",getpid());
18 }
19 else if(pid==0){
20 while(1){
21 printf("this is son pid %d, ppid %d\n",getpid(),getppid());//getppid获取父进程pid
22 sleep(1);
23 cnt++;
24 if(cnt>=5)
25 {
26
27 exit(3);
28 }
29 }
30 }
31
32
33 return 0;
34 }
运行结果:
chen@chen-virtual-machine:~/xt$ gcc demo16.c
chen@chen-virtual-machine:~/xt$ ./a.out
F pid 5844
this is father pid 5844
this is son pid 5845, ppid 5844//父进程提前结束,此时子进程变孤儿进程
chen@chen-virtual-machine:~/xt$ this is son pid 5845, ppid 1979//此时子进程被init进程收留,变成孤儿进程的父进程
this is son pid 5845, ppid 1979
this is son pid 5845, ppid 1979
this is son pid 5845, ppid 1979
5.exec族函数
原文链接: https://blog.csdn.net/u014530704/article/details/73848573
exec族函数函数的作用:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
exec族函数定义:
可以通过这个网站查询:linux函数查询
功能:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe
函数原型:
#include <unistd.h>
extern char **environ;
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 execvpe(const char *file, char *const argv[],char *const envp[]);
返回值:
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
下面将exac函数归为带l、带p、带v、带e 四类来说明参数特点。
一、 带l的一类exac函数(l表示list),包括execl、execlp、execle,要求将新程序的每个命令行参数都说明为 一个单独的参数。这种参数表以空指针结尾。
以execl函数为例子来说明:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(void)
6 {
7 printf("before execl\n");
8 if(execl("./echoarg","echoarg","abc",NULL) == -1)
9 {
10 printf("execl failed!\n");
11 perror("why");//若失败,调出失败原因
12 }
13 printf("after execl\n");r
14 return 0;
15 }
1 #include<stdio.h>
2
3 int main(int argc,char *argv[])
4 {
5 int i;
6 for(i=0;i<argc;i++)
7 {
8 printf("argv[%d]: %s\n",i,argv[i]);
9
10 }
11
12 return 0;
13 }
运行结果
chen@chen-virtual-machine:~/xt$ ./a.out
before execl
argv[0]: echoarg
argv[1]: abc
实验说明:
我们先用gcc编译echoarg.c,生成可执行文件echoarg并放在当前路径bin目录下。文件echoarg的作用是打印命令行参数。然后再编译execl.c并执行execl可执行文件。用execl 找到并执行echoarg,将当前进程main替换掉,所以”after execl” 没有在终端被打印出来。
二、 带p的一类exac函数,包括execlp、execvp、execvpe,如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。举个例子,PATH=/bin:/usr/bin
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(void)
6 {
7 printf("this pro is get system date\n");
8 if(execlp("date","date",NULL) == -1)
9 {
10 printf("execl failed!\n");
11 perror("why");
12 }
13 printf("after execl\n");
14 return 0;
15 }
运行结果
chen@chen-virtual-machine:~/xt$ ./a.out
this pro is get system date
2023年 08月 27日 星期日 11:51:40 CST
三、 带v不带l的一类exac函数,包括execv、execvp、execve,应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
如char *arg[]这种形式,且arg最后一个元素必须是NULL,例如char *arg[] = {“ls”,”-l”,NULL};
下面以execvp函数为例说明:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(void)
6 {
7 printf("this pro is get system ps\n");
8 char *argv[]={"ps","-l",NULL};
9 if(execvp("ps",argv) == -1)//execv与之区别则是path部分要加路径
10 {
11 printf("execl failed!\n");
12 perror("why");
13 }
14 printf("after execl\n");
15 return 0;
16 }
运行结果
chen@chen-virtual-machine:~/xt$ gcc demo23.c
chen@chen-virtual-machine:~/xt$ ./a.out
this pro is get system ps
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 3130 3125 0 80 0 - 6067 wait pts/20 00:00:00 bash
0 R 1000 4305 3130 0 80 0 - 7664 - pts/20 00:00:00 ps
四 exec配合fork使用
程序内容:不断检测用户输入,当用户输入1时,创建子进程,父进程继续等待输入,子进程调用exec函数,exec函数调用当前目录下的Change程序修改配置文件config.txt
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <sys/wait.h>
5 #include<stdlib.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <string.h>
9 int main()
10 {
11 pid_t pid;
12 int data;
13 while(1)
14 {
15 printf("please input data\n");
16 scanf("%d",&data);
17 if(data==1)
18 {
19 pid=fork();
20 if(pid>0)
21 {
22
23 }else if(pid==0)
24 {
25 if(execl("./changData","changdata","config.txt",NULL)==-1)
26 {
27 printf("errol\n");
28 perror("why");
29 }
30 }
31
32 }else
33 {
34 printf("wait input data\n");
35 }
36
37
38 }
39
40 return 0;
41 }
修改文件Long参数
ChangeData.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdlib.h>
8
9
10 int main(int argc ,char** argv)
11 {
12 int fdsrc;
13 int fddes;
14 char* readbuf=NULL;
15 if(argc!=2)
16 {
17 printf("pararm error\n");
18 exit(-1);
19 }
20
21 fdsrc= open(argv[1],O_RDWR);
22 int size=lseek(fdsrc,0,SEEK_END);
23 lseek(fdsrc,0,SEEK_SET);
24 readbuf=(char*)malloc(sizeof(char)*size+8);
25 int n_read=read(fdsrc,readbuf,size);
26
27 char* p=strstr(readbuf,"Long");
28 p=p+strlen("Long")+1;
29 *p='5';
30
31 fddes=open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0600);
32 int n_write=write(fddes,readbuf,strlen(readbuf));
33
34
35 close(fdsrc);
36 close(fddes);
37 return 0;
38 }
运行结果:
chen@chen-virtual-machine:~/xt$ cat config.txt
TSXT=45
LLL=4
Long=9
BBB=4
chen@chen-virtual-machine:~/xt$ gcc demo27.c
chen@chen-virtual-machine:~/xt$ gcc demo26.c
chen@chen-virtual-machine:~/xt$ ./a.out
please input data
1
please input data
^C
chen@chen-virtual-machine:~/xt$ cat config.txt
TSXT=45
LLL=4
Long=5
BBB=4
6.system函数
#include <stdlib.h>
int system(const char *command);
system()函数的返回值如下:
成功,则返回进程的状态值;
当sh不能执行时,返回127;
失败返回-1;
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(void)
6 {
7 printf("this pro is get system ps\n");
8 if(system("ps -l") == -1)
9 {
10 printf("execl failed!\n");
11 perror("why");
12 }
13 printf("after execl\n");
14 return 0;
15 }
运行结果
chen@chen-virtual-machine:~/xt$ gcc demo28.c
chen@chen-virtual-machine:~/xt$ ./a.out
this pro is get system ps
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 3130 3125 0 80 0 - 6067 wait pts/20 00:00:00 bash
0 T 1000 4834 3130 0 80 0 - 1088 signal pts/20 00:00:00 a.out
0 Z 1000 4835 4834 0 80 0 - 0 - pts/20 00:00:00 ch <defunct>
0 S 1000 5129 3130 0 80 0 - 1088 wait pts/20 00:00:00 a.out
0 S 1000 5130 5129 0 80 0 - 1126 wait pts/20 00:00:00 sh
0 R 1000 5131 5130 0 80 0 - 7664 - pts/20 00:00:00 ps
after execl
7.popen函数
比system在应用中的好处:可以获取运行的输出结果
popen函数是一个在C语言中使用的函数,它通过创建一个管道和调用fork函数来创建一个子进程,并执行一个shell命令来开启一个进程。父进程可以通过管道与子进程进行通信,要么从管道中读取信息,要么向管道中写入信息,具体取决于调用popen函数时传递的参数。popen函数返回一个文件指针,父进程可以使用这个文件指针来读取子进程的输出设备或写入到子进程的标准输入设备中。当不再需要使用管道时,可以使用pclose函数关闭由popen建立的管道和文件指针。popen函数和pclose函数的具体定义和使用方法可以参考相关的C语言库函数文档或man手册。
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(void)
6 {
7 FILE *fp;
8 char ret[1024]={0};
9 fp=popen("ps","r");
10 int nread= fread(ret,1,1024,fp);
11 printf("read size %d\nret=%s\n",nread,ret);
12 return 0;
13 }
运行结果
chen@chen-virtual-machine:~/xt$ gcc demo29.c
chen@chen-virtual-machine:~/xt$ ./a.out
read size 222
ret= PID TTY TIME CMD
3130 pts/20 00:00:00 bash
4834 pts/20 00:00:00 a.out
4835 pts/20 00:00:00 changData <defunct>
5222 pts/20 00:00:00 a.out
5223 pts/20 00:00:00 sh
5224 pts/20 00:00:00 ps