system()函数启用新进程占有原进程的文件描述符表的问题

我在A程序中占用了/dev/video0这个独占模式的设备文件,在A中用system函数启用了B程序,B程序的代码中并不包含对/dev/video0的访问,但是我发现B程序也占用了/dev/video0,并且我在A程序中关闭了/dev/video0后,A程序不再占用/dev/video0,但是发现B程序依然占用着/dev/video0这是为什么呢?

当你在A程序中打开/dev/video0并启动B程序时,B程序继承了A程序打开的所有文件描述符,包括/dev/video0的文件描述符。这就导致了B程序也可以访问/dev/video0,即使B程序的代码中并不包含对/dev/video0的访问。

当A程序关闭/dev/video0时,只是让A程序不再使用该设备,但是并没有真正地释放该设备的控制权,因为B程序依然在使用它。因此,当你尝试再次打开/dev/video0时,你会得到“设备忙”的错误提示,因为此时该设备已经被B程序占用,而不是被A程序占用。要想让B程序释放/dev/video0,你需要杀死B程序或让B程序自行关闭/dev/video0。

我的目的是在A中启用B程序,并且在A中可以杀死B,并且B不占用/dev/video0,那么有什么办法呢?
您可以考虑在A程序中使用fork()函数创建一个子进程,然后在子进程中调用B程序。这样,子进程和B程序之间是独立的进程,不会共享A程序的资源,包括/dev/video0。同时,由于子进程和A程序是不同的进程,所以A程序可以通过杀死子进程的方式来结束B程序的执行。
示例代码如下:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main() {
    
    
    pid_t pid = fork();

    if (pid == -1) {
    
    
        // fork() failed
        cerr << "Failed to fork()" << endl;
        return 1;
    } else if (pid == 0) {
    
    
        // child process
        execl("/path/to/B", "B", NULL);
        cerr << "Failed to execute B" << endl;
        return 1;
    } else {
    
    
        // parent process
        // wait for child process to terminate
        int status;
        waitpid(pid, &status, 0);

        if (WIFEXITED(status)) {
    
    
            cout << "Child process exited with status " << WEXITSTATUS(status) << endl;
        }
    }

    return 0;
}

在子进程中使用execl()函数调用B程序。注意,execl()函数的第一个参数是B程序的路径,第二个参数是B程序的名称,第三个参数是一个空指针。execl()函数调用成功后,子进程会替换成B程序的进程,而且B程序与A程序是独立的进程,不会共享任何资源。

在父进程中使用waitpid()函数等待子进程结束,并获取子进程的退出状态。如果子进程正常结束,可以通过WEXITSTATUS()宏获取子进程的退出码。

另外,如果您需要在A程序中占用/dev/video0这个设备文件,建议您在打开和关闭设备文件时加上文件锁,避免其他进程对设备文件的非法访问。

execl()系列函数来实现相同的目的。execl()系列函数会用指定的可执行程序替换当前进程,所以它不会创建新的进程,也不会复制原来进程的文件描述符表。
execl()函数会用指定的可执行程序来替换当前进程,如果替换成功,那么之后的代码将会在新的程序中执行,如果替换失败,则execl()函数会直接返回,并且不会继续执行后面的代码。
在调用execl()函数时,当前进程的代码段、数据段等内存区域会被指定的可执行程序替换,即当前进程会被新的程序所取代,新程序从main()函数开始执行,原先的程序和它所占用的内存空间都会被释放掉,也就是说,调用execl()函数后,当前进程的程序完全变成了新程序。这种方式常常用于实现进程的程序替换,比如常用的Unix命令行工具,如ls、grep、ps等等,这些工具其实都是通过execl()等函数实现的。

不是同一个进程id号。使用fork()创建新进程后,新进程会复制父进程的地址空间、文件描述符表等资源,但是会拥有一个新的进程id号。然后在新进程中使用execl()函数执行B程序时,新进程的地址空间、文件描述符表等都会被替换为B程序的内容,但是进程id号不会改变。所以A程序和B程序是通过两个不同的进程id号来运行的。

使用 vfork() 函数创建新进程时,新进程会共享父进程的地址空间和部分资源,包括文件描述符、共享内存和信号处理函数等。但是,由于 vfork() 函数不会像 fork() 函数一样完全复制父进程的地址空间,所以需要特别注意以下几点:

新进程和原来的父进程有不同的进程ID。在 vfork() 函数创建的新进程中,子进程的进程ID会被设置为0。一旦子进程开始执行,它会通过调用 exec() 函数来加载新的程序代码并替换自己的地址空间,此时子进程的进程ID将会改变。

父进程和子进程共享地址空间。由于新进程和原来的父进程共享同一地址空间,因此它们可以直接访问相同的变量和函数。这也意味着,如果子进程修改了某个变量的值,父进程中对应的变量值也会发生改变。

子进程会阻塞父进程。在 vfork() 函数创建新进程时,父进程会被阻塞,直到子进程执行完毕或者调用 exec() 函数为止。因此,在使用 vfork() 函数时,需要确保子进程能够尽快执行完毕,否则可能会导致父进程长时间被阻塞。

综上所述,虽然新进程和原来的父进程共享部分资源和文件描述符,但是它们的进程ID是不同的,同时也需要注意避免在子进程中修改父进程的数据,以及确保子进程能够尽快执行完毕,以避免对父进程的阻塞。

猜你喜欢

转载自blog.csdn.net/weixin_43466192/article/details/129169503