目录
相关指令
ps指令
top指令
相关函数及其示例
getpid()
getpid()示例:
fork()
示例:
示例2:fork()返回值
Father's pid is 14532.This is FartherProcess, pid is 14532.This is SonProcess, pid is 14533.
vfork()
示例:与fork()区分
exit();_exit();_Exit();
abort()
wait()
示例:使用fork();wait();exit();
waitpid()
概念:孤儿进程
示例:getppid()可以获取父进程id
exec族函数
execl()
示例:调用一个不存在的文件
示例:使用execl()调用ls指令
示例:获取系统时间
execlp()
示例:execlp()调用ps指令
如何添加文件到环境变量呢?
execv()
system()
popen()
示例:使用popen()调用ls指令并获取其输出结果
相关指令
ps指令
ps -aux 查看所有进程
ps -aux|grep init 筛选所有名字带init的进程
top指令
类似win的任务管理器
相关函数及其示例
getpid()
获取当前线程的id
AMEgetpid, getppid - get process identificationSYNOPSIS#include <sys/types.h>#include <unistd.h>pid_t getpid(void);pid_t getppid(void);
getpid()示例:
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){pid_t pid;pid = getpid();printf("My pid is %d.\n",pid);return 0;}
输出:
My pid is 14142.
fork()
在调用出生成一个子进程。过去,创建子进程会直接复制一份父进程的内存空间,但后来经过改进,父子进程在存储空间上使用写时拷贝,即子进程对父进程的内存空间为只读,当需要操作某内存时,会将其拷贝。返回值0则为子进程,非负数父进程,调用失败-1。
NAMEfork - create a child processSYNOPSIS#include <sys/types.h>#include <unistd.h>pid_t fork(void);
示例:
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){pid_t pid;pid = getpid();fork();printf("My pid is %d.\n",pid);return 0;}
输出
My pid is 14258.
My pid is 14258.
pid在fork之前get,所以两次打印都是父进程pid
所以这样写
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){pid_t pid;pid = getpid();fork();printf("Father's pid is %d.\n",pid);printf("My pid is %d.\n",getpid());return 0;}
输出:
Father's pid is 14289.
My pid is 14289.
Father's pid is 14289.
My pid is 14290.
示例2:fork()返回值
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){pid_t pid,pFork;pid = getpid();printf("Father's pid is %d.\n",pid);pFork = fork();if(pFork > 0){printf("This is FartherProcess, pid is %d.\n",getpid());}else if(!pFork){printf("This is SonProcess, pid is %d.\n",getpid());}return 0;}
Father's pid is 14532.
This is FartherProcess, pid is 14532.
This is SonProcess, pid is 14533.
vfork()
vfork直接使用父进程内存空间,不拷贝。vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。
AMEvfork - create a child process and block parentSYNOPSIS#include <sys/types.h>#include <unistd.h>pid_t vfork(void);Feature Test Macro Requirements for glibc (see fea‐ture_test_macros(7)):vfork():Since glibc 2.12:(_XOPEN_SOURCE >= 500) && ! (_POSIX_C_SOURCE >= 200809L)|| /* Since glibc 2.19: */ _DEFAULT_SOURCE|| /* Glibc versions <= 2.19: */ _BSD_SOURCEBefore glibc 2.12:_BSD_SOURCE || _XOPEN_SOURCE >= 500
示例:与fork()区分
使用fork():
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(){pid_t pid,pFork;pid = getpid();printf("Father's pid is %d.\n",pid);pFork = fork();while(1){sleep(1);if(pFork > 0){printf("This is FartherProcess, pid is %d.\n",getpid());}else if(!pFork){printf("This is SonProcess, pid is %d.\n",getpid());}}return 0;}
输出:都可以运行
查看进程 都为S+ ,都在运行
若改为使用vfork(),让子进程运行三秒退出:
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int main(){int cnt = 0;pid_t pid,pFork;pid = getpid();printf("Father's pid is %d.\n",pid);pFork = vfork();while(1){sleep(1);if(pFork > 0){printf("This is FartherProcess, pid is %d.\n",getpid());}else if(!pFork){printf("This is SonProcess, pid is %d.\n",getpid());cnt++;if(cnt == 3) exit(0);}}return 0;}
运行:子进程运行了3次,子进程退出之前父进程阻塞,子进程退出之后,父进程才开始运行。
但是可以注意到一个现象,子进程退出后,子进程会变为Z+,即Zombie僵尸进程。
所以引出我们在进程编程中的一个问题,即 父进程等待子进程退出 并收集子进程退出状态 ,如果子进程退出状态不被收集,变成僵尸进程。
exit();_exit();_Exit();
NAMEexit - cause normal process terminationSYNOPSIS#include <stdlib.h>void exit(int status);
NAME_exit, _Exit - terminate the calling processSYNOPSIS#include <unistd.h>void _exit(int status);#include <stdlib.h>void _Exit(int status);Feature Test Macro Requirements for glibc (see fea‐ture_test_macros(7)):_Exit():_ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L
abort()
NAMEabort - cause abnormal process terminationSYNOPSIS#include <stdlib.h>void abort(void);
wait()
NAMEwait, waitpid, waitid - wait for process to change stateSYNOPSIS#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *wstatus);pid_t waitpid(pid_t pid, int *wstatus, int options);int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);/* This is the glibc and POSIX interface; seeNOTES for information on the raw system call. */
示例:使用fork();wait();exit();
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main(){int cnt = 0;pid_t pid,pFork;pid = getpid();printf("Father's pid is %d.\n",pid);pFork = fork();wait(NULL);while(1){sleep(1);if(pFork > 0){printf("This is FartherProcess, pid is %d.\n",getpid());}else if(!pFork){printf("This is SonProcess, pid is %d.\n",getpid());cnt++;if(cnt == 3) exit(0);}}return 0;
}
现象:使用fork后正常应该父子进程都在运行,但是因为wait,所以父进程等待子进程退出。
(如果使用waitpid(),一个选项可以使父进程不阻塞)
也不会产生僵尸进程
那么,status这个参数又是什么?
status为状态参数:子进程退出状态放在它所指向的地址中,空则为不关心其退出状态
示例:让我们试着传递status,记得想读status,要用 WEXITSTATUS() 这个宏。
关于这个宏不多看,man 2 wait,就知道为什么了,可以顺带看看其它宏干嘛的
代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main(){int cnt = 0;int status = 0;pid_t pid,pFork;pid = getpid();printf("Father's pid is %d.\n",pid);pFork = fork();wait(&status);if(pFork > 0) printf("ChildProcess exit, exit_status : %d.\n",WEXITSTATUS(status));while(1){sleep(1);if(pFork > 0){printf("This is FartherProcess, pid is %d.\n",getpid());}else if(!pFork){printf("This is SonProcess, pid is %d.\n",getpid());cnt++;if(cnt == 3) exit(3);}}return 0;
}
运行:读取到状态值为3
waitpid()
上面刚刚提到了使用waitpid(),一个选项可以使父进程不阻塞
同理man 2 wait,option怎么填,WNOHANG就是不阻塞,大家可以自行实验。
The value of options is an OR of zero or more of the following constants:WNOHANGreturn immediately if no child has exited.WUNTRACEDalso return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped isprovided even if this option is not specified.WCONTINUED (since Linux 2.6.10)also return if a stopped child has been resumed by delivery of SIGCONT.(For Linux-only options, see below.)
概念:孤儿进程
父进程在子进程退出之前就退出了,子进程会变为孤儿进程;Linux为避免系统存在过多孤儿进程,init进程将收留孤儿进程,变成其父进程。
示例:getppid()可以获取父进程id
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main(){int cnt = 0;int status = 0;pid_t pid,pFork;pid = getpid();printf("Father's pid is %d.\n",pid);pFork = fork();while(1){sleep(1);if(pFork > 0){cnt++;if(cnt == 3) exit(0);}else if(!pFork){printf("SonProcess' pid is %d,my father's pid : %d.\n",getpid(),getppid());}}return 0;
}
现象:父进程退出后,子进程的父进程变成了1519
kill ‘pid’ 指令可以终止这个子进程
exec族函数
Linux系统编程中很重要的一系列函数
linux进程---exec族函数(execl, execlp, execle, execv, execvp, execvpe)_分别用execlp()、execl()和execv()函数实现命令“find / -name abc-CSDN博客
总结来说就是在调用进程内部执行一个文件
NAMEexecl, execlp, execle, execv, execvp, execvpe - execute a fileSYNOPSIS#include <unistd.h>extern char **environ;int execl(const char *pathname, const char *arg, .../* (char *) NULL */);int execlp(const char *file, const char *arg, .../* (char *) NULL */);int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);int execv(const char *pathname, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[],char *const envp[]);
execle和execvpe初学用的少,涉及到对环境变量的修改。
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
execl()
需要文件路径,不知道可以使用 whereis XXX 指令来寻找
示例:调用一个不存在的文件
使用了execl() 和 perror()
//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);int main(void)
{printf("before execl\n");if(execl("/bin/echoarg","echoarg",NULL,NULL) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0;
}
输出:
before execl
execl failed!
why: No such file or directory
after execl
示例:使用execl()调用ls指令
//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);int main(void)
{printf("before execl\n");if(execl("/bin/ls","ls",NULL,NULL) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0;
}
运行:成功调用ls,并且 after execl为被打印,因为调用之后进程被调用文件取代
示例:获取系统时间
将ls换为date即可,可以看见执行了date指令
execlp()
可以直接在环境变量中寻找,不需要加路径。
示例:execlp()调用ps指令
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("before execlp\n");if(execlp("ps","ps","-l",NULL) == -1){printf("execl failed!\n");perror("why");}printf("after execlp\n");return 0;
}
如何添加文件到环境变量呢?
echo $PATH 查看环境变量
pwd 查看当前目录
export PATH=$PATH:你要添加到环境变量的目录
可以看到添加了之后,我就可以用我自己实现的cp指令复制文件了(此实现详见我上一篇文章 文件编程)
execv()
区别在于使用数组指针传参
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{char *argv[] = {"cp", "file", "file1",NULL};printf("before execv\n");if(execv("/bin/cp",argv) == -1){printf("execv failed!\n");perror("why");}printf("after execv\n");return 0;
}
system()
更加简单粗暴
system()函数的返回值如下: 成功,则返回进程的状态值; 当sh不能执行时,返回127; 失败返回-1;
NAMEsystem - execute a shell commandSYNOPSIS#include <stdlib.h>int system(const char *command);
示例:使用system()调用rm指令删除一个文件
#include <stdio.h>
#include <stdlib.h>int main(void)
{if(system("rm file1") == -1){printf("fail\n");}return 0;
}
popen()
比system()的优势:可以获取程序执行的结果
popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。
这个进程必须由 pclose() 函数关闭,而不是 fclose() 函数。pclose() 函数关闭标准 I/O 流,等待命令执行结束,然后返回 shell 的终止状态。如果 shell 不能被执行,则 pclose() 返回的终止状态与 shell 已执行 exit 一样。
也就是,popen创建管道,执行shell命令将文件流中的某些数据读出
NAMEpopen, pclose - pipe stream to or from a processSYNOPSIS#include <stdio.h>FILE *popen(const char *command, const char *type);int pclose(FILE *stream);
参数说明:
command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令,比如sh -c ls
type: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。
返回值:
如果调用 fork() 或 pipe() 失败,或者不能分配内存将返回NULL,否则返回一个读或者打开文件的指针。
示例:使用popen()调用ls指令并获取其输出结果
#include <stdio.h>
#include <unistd.h>int main(int argc ,char **argv){char ret[1024] = {0};FILE *fp;//FILE *popen(const char *command, const char *type);fp = popen("ls -l","r");//size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);int nread = fread(ret,1,1024,fp);printf("read ret %d byte,\n%s\n",nread,ret);return 0;
}
运行结果