欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

unix核心编程原理(四)

程序员文章站 2022-05-24 07:50:14
...

                                                                                进程与输入输出重定向

shell是如何运行程序的?
根据用户输入的字符串进行解析后,传给execvp系统函数,启动一个新的进程。比如execvp("ls",arglist)。不过exec(execvp是一组基于execve系统调用函数中的一个,他们统称为exec)系统调用从当前进程中把当前程序的机器指令清除,然后在空的进程中载入调用时指定的代码,最后运行这个新的程序。exec调整进程的内存分配使之适应新的程序对内存的要求。相同的进程,不同的内容。(但值得注意的是,子进程继承了父进程的一些资源,比如打开的文件描述符等,子进程同样可以进行使用。)


显然如果这样做,虽然启动了想执行的进程,当由于是在shell进程空间内执行的,清除了shell的内容,导致shell进程消亡了,如果想执行另一个命令,有需要启动新的shell,这显然达不到我们的要求 。解决方案就是,产生一个不影响shell进程的进程。可以调用系统函数fork(),该函数分配一个新的空间完全复制一个和调用它的进程完全一样的进程,但是新的进程只能从fork()方法之后开始运行。然后再该新进程中调用exec添加要执行的进程。当一个进程调用fork后,就有两个二进制代码完全相同的进程,而且他们都运行到相同的地方。可以通过控制fork()函数的返回值,使得两个进程开始它们自己的旅程。
代码如下:
main(){

int ret_from_fork,mypid;
mypid = getPid();
printf("before:my pid is d%\n",mypid);
ret_from_fork = fork();//该方法返回生成的子进程的进程id号。用于复制出一个进程后,他们都运行到同样的地方,所以父进程中的ret_from_fork的值是id值,而不时初值0,而子进程的ret_from_fork却没有获得值,还是0.通过这样就可以区别两个进程改变两个进程的走向。
switch(ret_from_fork){

case -1:
perror(" fork failed");
exit(1);
//以下就是子进程要执行的代码,他调用exec载入用户输入的命令指定的程序,清除进程空间执行用户指定的程序。
case 0:
execvp(arglist[0],arglist);//arglist[0]中指定用户想执行的命令名。
perror("execvp failed");
exit(1);

default:
while(wait(&exitstatus)!=ret_from_fork);
//shell程序,等待子进程运行结束后,再接受用户输入
}

}

通过以上方法就可以起到一个新的指定进程,而不会影响现在的进程。



输入输出重定向问题:

所有的Unix I/O 重定向都基于标准数据流的原理。
首先讨论重定向到文件:(通常运行的命令都是从标准输入流(键盘)获得程序输入,将执行的结果通过标准输出流打印到显示器上)
三个标准流:标准输入流,标准输出流,标准错误输出流。这三个流的每一种都是一个特别的文件描述符,分别对应:
    0:stdin
    1:stdout
    2:stderr

   
   
Unix假设文件描述符0,1,2都已经打开,可以分别进行读,写操作了。大多数程序并不接收输出文件名,他们总是将结果写到文件描述符1,并将错误消息写到文件描述符2.所以要想将进程的输出写到文件或另一个进程的输入去,就必须重定向相应的文件描述符。可以将文件描述符1指向你想输出的文件,这样就达到了重定向的目的。虽然程序仍然向标准输出写,当却不知道输出的东西已不是留到标准输出了,而是留到了重定向的文件中去了。现在就要考虑如何实现将文件描述符与重定向文件关联起来。在此之前,先让我们了解"最低可用文件描述符"原则:当打开文件时,为此文件安排的描述符总是此数组中最低可用位置的索引。进程并不是从文件读取数据,而是从文件描述符读数据。如果将文件描述符0定位到一个文件,那么此文件就成为标准输入的源。
    有二种方法实现重定位:
    1,close then open
  调用close(0)将标准输入的连接挂断。然后使用open打开一个想连接到的文件。当前的最低可用文件描述符是0,因此所打开的文件将被连接到标准输入上去。
    2,open close dup close
    该方法采用的原理类似,从楼上的电话复制一个连接到楼下,然后就可以在不断线的情况下将楼上的连接切断。
    fd=open(file),获得描述符fd不是0,因为0已经被打开了。
    close(0),将文件描述符0关闭。0现在已经空闲了。
    dup(fd),将文件描述符fd做了一个复制。此次复制使用最低可用文件描述符号,因此获得的文件描述符是0.这样就将磁盘文件与文件描述符0连接在一起了。
    close(fd),关闭文件的原始连接。只留下文件描述符0的连接。


要实现进程之间的I/O重定向,道理差不多,但是要使用管道。将管道的两端与输入输出挂接即可。
当进程创建了一个管道之后,该进程就有了连向管道两端的连接。当这个进程调用fork()的时候,他的子进程也得到了这连个连向管道的连接。父进程和子进程都可以将数据写到管道的写数据端口,并从读数据端口读出。两个进程都可以读写管道,但一个写,一个读,效率是最高的。

相关标签: 编程 Unix