pclose与fclose的区别

先写结论pclose会调用waitpid为popen时fork的子进程收尸,而fclose不会。所以如果调用popen后用fclose关闭,那么将可能产生僵尸进程。

一段丑陋的测试代码:

#include
#include 
ma()
{
    FILE * fp;
    char buffer[80];
    fp=popen("cat /etc/cwmp.config","r");
    fgets(buffer,sizeof(buffer),fp);
    printf("%s",buffer);
    fclose(fp);     // fclose关闭
}
main()
{
    int pid = 0;

    ma();
    ma();
    ma();
    sleep(10);
}

程序放后台执行,然后用pstree查看,就可以看到产生了三个僵尸进程,程序退出后,僵尸进程才被父父进程收尸退出。

下面看一下popen和pclose在uclibc中的源码

static struct popen_list_item *popen_list /* = NULL (bss initialized) */;

FILE *popen(const char *command, const char *modes)
{
	FILE *fp;
	struct popen_list_item *pi;
	struct popen_list_item *po;
	int pipe_fd[2];
	int parent_fd;
	int child_fd;
	int child_writing;			/* Doubles as the desired child fildes. */
	pid_t pid;

	child_writing = 0;			/* Assume child is writing. */
	if (modes[0] != 'w') {		/* Parent not writing... */
		++child_writing;		/* so child must be writing. */
		if (modes[0] != 'r') {	/* Oops!  Parent not reading either! */
			__set_errno(EINVAL);
			goto RET_NULL;
		}
	}

	if (!(pi = malloc(sizeof(struct popen_list_item)))) {
		goto RET_NULL;
	}

	if (pipe(pipe_fd)) {
		goto FREE_PI;
	}

	child_fd = pipe_fd[child_writing];
	parent_fd = pipe_fd[1-child_writing];

	if (!(fp = fdopen(parent_fd, modes))) {
		close(parent_fd);
		close(child_fd);
		goto FREE_PI;
	}

	VFORK_LOCK;
	if ((pid = vfork()) == 0) {	/* Child of vfork... */
		close(parent_fd);
		if (child_fd != child_writing) {
			dup2(child_fd, child_writing);
			close(child_fd);
		}

		/* SUSv3 requires that any previously popen()'d streams in the
		 * parent shall be closed in the child. */
		for (po = popen_list ; po ; po = po->next) {
			close(po->f->__filedes);
		}

		execl("/bin/sh", "sh", "-c", command, (char *)0);

		/* SUSv3 mandates an exit code of 127 for the child if the
		 * command interpreter can not be invoked. */
		_exit(127);
	}
	VFORK_UNLOCK;

	/* We need to close the child filedes whether vfork failed or
	 * it succeeded and we're in the parent. */
	close(child_fd);

	if (pid > 0) {				/* Parent of vfork... */
		pi->pid = pid;
		pi->f = fp;
		VFORK_LOCK;
		pi->next = popen_list;
		popen_list = pi;
		VFORK_UNLOCK;

		return fp;
	}

	/* If we get here, vfork failed. */
	fclose(fp);					/* Will close parent_fd. */

 FREE_PI:
	free(pi);

 RET_NULL:
	return NULL;
}

#warning is pclose correct wrt the new mutex semantics?

int pclose(FILE *stream)
{
	struct popen_list_item *p;
	int stat;
	pid_t pid;

	/* First, find the list entry corresponding to stream and remove it
	 * from the list.  Set p to the list item (NULL if not found). */
	VFORK_LOCK;
	if ((p = popen_list) != NULL) {
		if (p->f == stream) {
			popen_list = p->next;
		} else {
			struct popen_list_item *t;
			do {
				t = p;
				if (!(p = t->next)) {
					__set_errno(EINVAL); /* Not required by SUSv3. */
					break;
				}
				if (p->f == stream) {
					t->next = p->next;
					break;
				}
			} while (1);
		}
	}
	VFORK_UNLOCK;

	if (p) {
		pid = p->pid;			/* Save the pid we need */
		free(p);				/* and free the list item. */

		fclose(stream);	/* The SUSv3 example code ignores the return. */

		/* SUSv3 specificly requires that pclose not return before the child
		 * terminates, in order to disallow pclose from returning on EINTR. */
		do {
			if (waitpid(pid, &stat, 0) >= 0) {
				return stat;
			}
			if (errno != EINTR) {
				break;
			}
		} while (1);
	}

	return -1;
}

你可能感兴趣的:(c语言,linux)