程序和进程
程序是一个可执行文件,进程是程序的执行实例,unix系统中,每个进程都有一个唯一的数字标识符,被称为进程ID。
#include "apue.h" int main(void){ printf("hello world form process ID %ld\n",(long)getpid()); exit(0); }
重点:%ld表示打印长整型
getpid()函数,返回自身的进程id,返回值类型为pid_t,如果要输出,可以(long)强转成长整型,使用长整型可以提高可移植性,推测:防止某个运行环境整型过短的情况发生。
进程控制
有3个用于进程控制的主要函数:fork,exec,waitpid。
#include "apue.h" int main(void){ char buf[MAXLINE]; pid_t pid; int status; printf("%% "); while(fgets(buf,MAXLINE,stdin)!=NULL){ if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0;//replace the last char with null if((pid=fork())<0){ err_sys("fork error"); }else if(pid==0){ /*child*/ execlp(buf,buf,(char *)0); err_ret("couldn't execute:%s",buf); exit(127); } /*parent*/ if((pid=waitpid(pid,&status,0))<0) err_sys("wait pid error"); printf("%% "); } exit(0); }
该例程实现的功能是输入一个命令并执行,命令必须是$PATH指定的目录中存在的,且命令不接收参数。
重点:
fgets(buf,MAXLINE,stdin):从标准输入读入最大MAXLINE长度的内容到buf中,如果以换行符结尾,则将换行符替换为0(null),这是因为execlp要求的参数是以null结束而不是以换行符结束的。
fork():创建一个新进程,新进程是调用进程的一个副本,称为子进程,fork对父进程返回进程id,对子进程则返回0(这段写的太模糊了。。。),调用一次,返回两次。
注释中写了一个/*child*/和一个/*parent*/,推测这分别是父进程和子进程的执行内容,个人理解:此时有两个进程在工作,根据上文的写的“返回两次”,对子进程来说pid==0,那么执行/*child*/标注下的代码,对父进程来说pid==一个非负整数唯一标识,执行/*parent*/标注下的代码,等待子进程返回的结果。
execlp(buf,buf,(char *)0):第一个参数表示执行的文件路径,第二个表示arg0,即第0个参数,(例子:ls -l:ls就是第0个参数,-l是第1个参数),man一下这个函数,原型中最后一个参数必须为(char *)类型的空指针,这个我也不知道为啥,百度百科的解释是:“如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整形参数,如果一个整形数的长度与char * 的长度不同,那么exec函数的实际参数就将出错。”,也没说为啥出错。。。
更重要的一点,如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了。书里的解释是:用新的程序文件替换了子进程原先执行的程序文件。
pid_t waitpid(pid_t pid, int *stat_loc, int options);返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码。
在本例中,第二个参数为null,options为0,在子进程pid结束之前什么都不做。
5. 线程和线程ID
通常,一个进程只有一个控制线程(thread)——某一时刻执行的一组机器指令。
一个进程内所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。因为他们能访问同一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性。
一点新的知识:返回值的接收者是进程,在unix中,产生(spawn)一个进程意味着1.创建一个pid,2.执行一个可执行文件,如果没有后者,那么只是一个空进程(其实不晓得理解的对不对)。
欢迎看到本文的朋友们与小弟交流,或者指正文中的错误,共同进步。