进程系统调用
函数fork();
其原型pid_t fork(void);其在头文件
#include<sys/types.h>,#include<unistd.h>
这个函数的返回值:如果是出错返回-1;
如果是成功的话返回两次,对于父进程返回子进程的进程号,对于子进程返回0;
对于fork()这个函数,我这里要简要的分析一下:注意下面的言语是我自己理
解的仅供参考:
fork()解析:
分析一:
首先如果在一个程序中调用fork(),那么当前这进程就会作为一个父亲进程创建
一个子进程,这个创建的过程,实际上是把当前这个父进程的所有资源复制一份
给其子进程,但是,虽然是copy了一份“基因”,其功能也可能不一样,要根据
你自己的代码是怎样实现的
分析二:
在功能部分,我们把父进程和子进程看做是在同一起跑线的参赛者,开始谁线领
先,要根据裁判员(也就是系统,要根据当时的情况,所以程序员是不清楚谁先
运行),但是这个差别可以说是趋近与零,也就是父进程和子进程同时“起跑”
,如果父进程跑的漫的话(sleep(10),跑到一个位置休息10秒),那么子进程就
会超越父进程,因而其在此时其先于父进程执行其功能,如果执行了一段时间,
子进程也休息了(sleep(10)),但是此时父进程已经休息完了(即两者又在同一
起跑线),那么父进程又超越子进程,因而会先于子进程执行其功能部分,一直到
两者都完全跑到终点(全都执行完成,或者因为某种原因直接到了终点),此时
裁判员完成后续的操作(系统完成后续的操作)。
如下例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int glob = 6; /* external variable in initialized
data */
char buf[ ] = "a write to stdout\n";
//char buf[ ] = "a write to stdout";
int main(void)
{
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
if ((write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-
1))
{ printf("write error"); exit(1); }
printf("before fork\n"); /* we don't flush stdout */
//printf("before fork"); /* we don't flush stdout */
if ( (pid = fork()) < 0)
{ printf("fork error"); exit(2); }
else if (pid == 0) { /* child */
glob++; /* modify
variables */
var++;
} else
sleep(2); /* parent */
printf("pid = %d, ppid = %d, glob = %d, var = %d\n", getpid
(),getppid(), glob, var);
exit(0);
}
对于exec函数族:
这个函数族的重要功能在于:让自己执行新的程序;具体来讲就是,当进程认为
自己不能再为系统做任何贡献时,就可以调用exec函数,转而区执行其他的程序
其中有六个成员:其原型分别为:
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[]);
下面我用源码一一来实现对这几个函数的操作;
函数一:int execl(const char *path, const char *arg, ...);代码中有注释
,具体使用可以查man手册,这才是王道
1 #include<stdio.h>
2 #include<unistd.h>
3 int main(int argc,char *argv[]){
4 //对于execl(path,mend,...),对于第一个参数是你需要执行的二进制文件
的> 全路径名,第二个参数是你要执行的命令,或者说可执行文件,后面的参
数是> 可选的参数(就是你的可执行文件可能带有的参数,任意选取),但是
一定要NULL结束
5 execl("/bin/ls","ls","-l","-a",NULL);
6 printf("sorry\n");
7 }
函数二: int execlp(const char *file, const char *arg, ...);你只需给出
命令,或者可执行的文件,系统可以通过环境变量来查找
1 #include<stdio.h>
2 #include<unistd.h>
3 int main(int argc,char *argv[]){
4 //execlp()同execl()区别在于第一个参数是要执行的文件名,而非全路径,
也可以,可执行文件的路径
5 execlp("ls","ls","-l","-a",NULL);
6 printf("sorry\n");
7
9 }
函数三:int execle(const char *path, const char *arg,
..., char * const envp[]);可以传入环境变量
1 #include<stdio.h>
2 #include<unistd.h>
3 int main(int argc,char *argv[]){
4 char *env_t[]={"PATH=/bin/ls",NULL};
5 ///execle(),第一个参数是要执行文件的路径,第二个参数是要执行的命令
,> 后面的该命令,或者该可执行文件的可轩参数,知道NULL,最后一个参数
是该> 可执行文件要找的环境变量
6 execle("/bin/ls","ls",NULL,env_t);
7 printf("to here\n");
8 }
函数四:int execv(const char *path, char *const argv[]);表示将所有参数
构造成指针数组传递
1 #include<stdio.h>
2 #include<unistd.h>
3 int main(int argc,char *argv[]){
4 char *pass[3];
5 pass[0]="ls";
6 pass[1]="-l";
7 pass[2]=NULL; //NULL这里不能用双引号
8 //execv(const char *path,char *const argv[]);这里第一个参数表示你要
执
行的的可执行文件的全路径放进去
9 execv("bin/ls",pass);
10 printf("to here\n");
11 }
~
函数五: int execvp(const char *file, char *const argv[]);
~ 1 #include<stdio.h>
2 #include<unistd.h>
3 int main(int argc,char *argv[]){
4 char *pass[3];
5 pass[0]="ls";
6 pass[1]="-l";
7 pass[2]=NULL; //NULL这里不能用双引号
8 //这里execvp(const char *file,char *const argv[]);第一个参数,可以
死> 要执行命令的全路径,也可以是命令,他会到环境变量里面去找到相关的
命令
9 execvp("ls",pass);
10 printf("to here\n");
11 }
函数六:int execve(const char *path,char *const argv[],char *const
envp[]);
1 #include<stdio.h>
2 #include<unistd.h>
3 int main(int argc,char *argv[]){
4 char *pass[3];
5 pass[0]="ls";
6 pass[1]="-l";
7 pass[2]=NULL;
8 char *env_t[]={"PATH=/bin/ls",NULL};
9 execve();第一个参数是全路径,第二个参数是命令,第三个参数是该命
令
的的环境变量路径
10 execve("/bin/ls",pass,env_t);
11 printf("to here\n");
12
13
14 }
对于wait()函数:
其原型:pid_t wait(int *status);
函数说明:wait()会暂时停止目前进程的执行, 直到有信号来到或子进程结束.
如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值.
子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一快返回
. 如果不在意结束状态值, 则参数 status 可以设成NULL. 子进程的结束状态值
请参考waitpid().
下面是我的一个小shell(非原创):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include
<fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include
<sys/wait.h>
#include <string.h>
int parseargs(char * cmdline);
char
*cmdargv[20] = {0};
int main(void)
{
pid_t pid;
char buf[100];
int retval = 1;
printf("WoLaoDa# ");
fflush(stdout);
while (1) {
fgets(buf, 100, stdin);
buf
[strlen(buf) - 1] = '\0';
if ((pid = fork()) < 0) {
perror("fork");
exit(-1);
} else if (pid == 0) {
sleep(5);
parseargs(buf);
execvp(cmdargv[0],
cmdargv);
exit(0);
}
wait(&retval);
//printf("retval = %d\n", retval);
printf("WoLaoDa# ");
fflush(stdout);
}
}
int
parseargs(char * cmdline)
{
char *head, *tail, *tmp;
int i;
head = tail = cmdline;
for( ; *tail == ' '; tail++)
;
head = tail;
for (i = 0; *tail != '\0'; i++) {
cmdargv[i] = head;
for( ; (*tail != ' ') && (*tail !=
'\0'); tail++)
;
if (*tail == '\0')
continue;
*tail++ = '\0';
for( ; *tail == ' '; tail++)
;
head =
tail;
}
cmdargv[i] = '\0';
return i;
}