Linux编程 函数和进程操作

来源:互联网 发布:安卓端口映射软件 编辑:程序博客网 时间:2024/06/05 21:07

1,linux进程操作

1.1 函数system

【调用fork】
system()会调用fork()产生子进程,由子进程来调用/bin/sh -c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。、

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 函数原型:    
  2. int system(const char * string);    
  3. 参数说明:    
  4. string:命令    
  5. int:返回值    
  6.   =-1:出现错误      
  7.   =0:调用成功但是没有出现子进程      
  8.   >0:成功退出的子进程的id(=127表示system()在调用/bin/sh时失败)  

1.2 函数exec

【替换进程映像】

exec家族一共有六个函数,分别是:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int execl(const char *path, const char *arg0, ...... ,(char*)0);    
  2. int execlp(const char *file, const char *arg0, ......,(char*)0);    
  3. int execle(const char *path, const char *arg0, ......,(char*)0,char * const envp[]);    
  4. int execv(const char *path, char *const argv[]);    
  5. int execvp(const char *file, char * const argv[]);    
  6. int execve(const char *filename, char *const argv[], char *const envp[]);   

1,其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。

2,几个字母:
"l"代表 list即表示以list形式给出,但最后要加一个空指针,如果用常数0来表示空指针,则必须将它强行转换成字符指针,否则有可能出错。
"v"代表 vector即矢量,
"p"代表通过PATH环境变量来查找新程序的可执行文件路径,
"e"代表可以传入一个环境变量envp。

3,示例程序:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. char *argv[]={“ls”,”-l”,(char *)0} //先定义一个指针数组    
  2. if (execv(“/bin/ls”,argv)) < 0)    
  3. {    
  4.     perror(“execl error!”);    
  5.     
  6. }  

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <unistd.h>    
  2. main()    
  3. {    
  4.     char * const ps_argv[]={"ps","-ux",(char*)0};    
  5.     char * const ps_envp[]={"PATH=/bin:/usr/bin","TERM=console",(char*)0};    
  6.     
  7.     //以下任选一种    
  8.     execl("/bin/ps","ps","-ux",(char*)0);    
  9.     execlp("ps","ps","-ux",(char*)0);    
  10.     execle("/bin/ps","ps","-ux",(char*)0,ps_envp);    
  11.     
  12.     execv("/bin/ps",ps_argv);    
  13.     execvp("ps",ps_argv);    
  14.     execve("/bin/ps",ps_argv,ps_envp);    
  15.         
  16.     printf("Done.\n");//不会被执行,因为替换后不再返回到原函数中    
  17. }   

执行替换后不再返回到原程序。

1.3 函数fork

【复制进程映像】

http://blog.csdn.net/jason314/article/details/5640969

由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 函数原型:    
  2. pid_t fork( void);    
  3. 参数说明     
  4. pid_t:返回值,成功 ==> 父进程:返回子进程id     
  5.                     ==> 子进程:返回0     
  6.                失败 ==> 返回-1   

常用程序结构:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <unistd.h>    
  2. #include <stdlib.h>    
  3. #include <sys/types.h>    
  4. #include <stdio.h>    
  5. int main()    
  6. {    
  7.     pid_t pid;    
  8.     printf("fork()");    
  9.     //printf("fork()\n");    
  10.     pid=fork();    
  11.     switch (pid)    
  12.     {    
  13.     case -1:/* error */    
  14.         perror("fork error\n");    
  15.         exit(1);    
  16.     case 0:/* child */    
  17.         printf("child   ppid:%4d pid:%4d return:%4d\n",getppid(),getpid(),pid);      
  18.         break;    
  19.     default:/* father */    
  20.         printf("father  ppid:%4d pid:%4d return:%4d\n",getppid(),getpid(),pid);      
  21.         break;    
  22.     }    
  23.     exit(0);    
  24. }   

结果如下显示:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #./fork2  #printf("fork()");    
  2. fork()child   ppid:17960 pid:17961 return:   0    
  3. fork()father  ppid:1084 pid:17960 return:17961、    
  4.     
  5. #./fork2  #printf("fork()\n");    
  6. fork()    
  7. child   ppid:17923 pid:17924 return:   0    
  8. father  ppid:1084 pid:17923 return:17924   

这就跟printf的缓冲机制有关了,printf某些内容时,操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上。但是,只要看到有/n 则会立即刷新stdout,因此就马上能够打印了
运行了printf("fork!")后,“fork!”仅仅被放到了缓冲里,程序运行到fork时缓冲里面的“fork!”  被子进程复制过去了。因此在子进程度stdout缓冲里面就也有了fork! 。所以,你最终看到的会是fork!  被printf了2次!!!!
而运行printf("fork! /n")后,“fork!”被立即打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有fork! 内容。因此你看到的结果会是fork! 被printf了1次!!!!

1.4 函数vfork

vfork创建新进程的主要目的在于用exec函数执行另外的程序,实际上,在没调用exec或exit之前子进程的运行中是与父进程共享数据段的。在vfork调用中,子进程先运行,父进程挂起,直到子进程调用exec或exit,在这以后,父子进程的执行顺序不再有限制

fork与vfork的区别:



如果没有_exit(0)的话,子进程没有调用exec 或exit,所以父进程是不可能执行的,在子进程调用exec 或exit 之后父进程才可能被调度运行。下面就来讲下exit和_exit的区别。当调用exit(0)以后,系统会清洗缓冲区,并且关闭全部的标准I/O流。而调用_exit(0)后缓冲区冲不冲洗得看具体实现,在UNIX系统中,是不清洗的。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>      
  2. #include <unistd.h>      
  3. #include <stdio.h>      
  4. #include <stdlib.h>    
  5.       
  6. int main()      
  7. {      
  8.     pid_t pid;      
  9.     int cnt = 0;      
  10.     pid = vfork();      
  11.     switch (pid)    
  12.     {    
  13.     case -1:/* error */    
  14.         perror("vfork error\n");    
  15.         exit(1);    
  16.     case 0:/* child */    
  17.         cnt++;      
  18.         printf("cnt=%d\n",cnt);      
  19.         printf("child   ppid:%4d pid:%4d return:%4d\n",getppid(),getpid(),pid);    
  20.         _exit(0);      
  21.         break;    
  22.     default:/* father */    
  23.         cnt++;      
  24.         printf("cnt=%d\n",cnt);      
  25.         printf("father  ppid:%4d pid:%4d return:%4d\n",getppid(),getpid(),pid);    
  26.         break;    
  27.     }     
  28.     exit(0);      
  29.       
  30. }   

结果:
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #./vfork     
  2. cnt=1    
  3. child   ppid:24884 pid:24885 return:   0    
  4. cnt=2    
  5. father  ppid:1084 pid:24884 return:24885   

网上抄的一段,可以再理解理解: 
为什么会有vfork,因为以前的fork 很傻, 它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,而往往在子进程中会执行exec 调用,这样,前面的拷贝工作就是白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与 
父进程共享地址空间(其实就是线程的概念了),因为这时候子进程在父进程的地址空间中运行,所以子进程不能进行写操作,并且在儿子 霸占”着老子的房子时候,要委屈老子一下了,让他在外面歇着(阻塞),一旦儿子执行了exec 或者exit 后,相 于儿子买了自己的房子了,这时候就相 于分家了。

1.5 函数wait

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 函数原型:    
  2. pid_t wait (int * status);    
  3. 参数说明:    
  4. status:子进程的结束状态值会由参数status 返回    
  5. pid_t:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中  

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

该函数我的理解是:为了避免出现僵尸进程(即父进程没有wait读取子进程的退出信息)

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include<sys/types.h>    
  2. #include<unistd.h>    
  3. #include<stdio.h>    
  4. #include<sys/wait.h>    
  5. #include<stdlib.h>    
  6.      
  7. int main()         
  8. {    
  9.     pid_t pid;    
  10.     char * message;    
  11.     int n;    
  12.     int exit_code;    
  13.     pid = fork();    
  14.     
  15.     switch(pid)    
  16.     {    
  17.     case -1 :     
  18.         perror("fork failed!\n");    
  19.         exit(1);    
  20.     case 0:       
  21.         message = "this is the child";    
  22.         n=5;    
  23.         exit_code=37;    
  24.         break;    
  25.     default:     
  26.         message = "this is the parent";    
  27.         n=3;    
  28.         exit_code=0;    
  29.         break;    
  30.       }    
  31.     
  32.     for(;n>0;n--){    
  33.         puts(message);    
  34.         sleep(1);    
  35.         
  36.     }    
  37.     //这里为了防止在退出时父进程先退出导致子进程成为僵尸进程,用wait    
  38.     if (pid != 0){    
  39.         int stat_val;    
  40.         pid_t child_pid = wait(&stat_val);    
  41.         printf("child has finished :PID=%d\n",child_pid);    
  42.         if (WIFEXITED(stat_val))    
  43.             printf("child exited with code %d\n",WEXITSTATUS(stat_val));    
  44.         else    
  45.             printf("child terminated abnormally\n");    
  46.     }    
  47.     exit(exit_code);    
  48. }   

1.6 函数waitpid

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 函数原型:    
  2. pid_t waitpid(pid_t pid,int * status,int options);    
  3. 参数说明:    
  4. pid<-1 等待进程组识别码为pid 绝对值的任何子进程。    
  5. pid=-1 等待任何子进程,相当于wait()。    
  6. pid=0 等待进程组识别码与目前进程相同的任何子进程。    
  7. pid>0 等待任何子进程识别码为pid 的子进程。    
  8. status:子进程的结束状态值会由参数status返回。    
  9. pid_t:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回    
  10. -1。失败原因存于errno 中。  

常用结构:
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //设置等待子进程,且父进程不阻塞    
  2. waitpid(child_pid,(int*)0,WNOHANG)   

WNOHANG表示非阻塞调用。
















0 0
原创粉丝点击