在C語言中,若要呼叫一個外部程式來進行想要的工作
ㄧ般都會使用system() 或 fork()+ exec() 的指令來建立一個子行程
使用此種方式建立的子行程會繼承父行程的很多的特性
其中很重要的一個就是檔案描述子(file descriptor) 的繼承。
這個特性在某些Hot plug的應用中會造成嚴重的問題,
舉例來說: 父行程(P1)開啟一個裝置檔取得fd1,之後呼叫system() 或 fork + exec 建立一個背景行程(P2)
那該fd 就會變成同時由父行程(P1)與子行程(P2)所參照(fd reference counter = 2)。
假設裝置此時被移除, 父行程發現後關閉fd1,但由於子行程仍持有fd1,
因此該檔案並不會真正被關閉,而是持續保持開啟, 直到子行程結束
這段期間裝置驅動程式無法被移除裝置檔
而造成新加入的裝置無法正確的被偵測到
要解決此問題的方法,就是在裝置檔開啟後,
立即透過fcntl 將該檔案描述子的述性設定為 close on exec
其API如下:
fcntl(fd, F_SETFD, FD_CLOEXEC);
這樣一來,在子行程建立的同時,子行程就會預先關閉該fd,
就可以避免fd 被子行程參照的問題
---------------------------------------------------------------------------------------------
关于fcntl(fd, F_SETFD, FD_CLOEXEC)设置exec时close的属性
snd_ctl_hw_open
#define SNDRV_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i"
sprintf(filename, SNDRV_FILE_CONTROL, card); // 路径/dev/snd/controlC0
fd = snd_open_device(filename, fmode);
fcntl(fd, F_SETFD, FD_CLOEXEC); // 这里设置为FD_CLOEXEC表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递给exec创建的新进程, 如果设置为fcntl(fd, F_SETFD, 0);那么本fd将保持打开状态复制到exec创建的新进程中[luther.gliethttp].
进入内核系统调用
sys_fcntl
do_fcntl
case F_SETFD:
err = 0;
set_close_on_exec(fd, arg & FD_CLOEXEC);
void fastcall set_close_on_exec(unsigned int fd, int flag)
{
struct files_struct *files = current->files;
struct fdtable *fdt;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);
if (flag)
FD_SET(fd, fdt->close_on_exec);
else
FD_CLR(fd, fdt->close_on_exec);
spin_unlock(&files->file_lock);
}
下面是man fcntl看到的对FD_CLOEXEC解释
File descriptor flags
The following commands manipulate the flags associated with a file descriptor. Currently, only one such flag is
defined: FD_CLOEXEC, the close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor will remain open
across an execve(2), otherwise it will be closed.
F_GETFD (void)
Read the file descriptor flags; arg is ignored.
F_SETFD (long)
Set the file descriptor flags to the value specified by arg.
snd_ctl_hw_open
#define SNDRV_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i"
sprintf(filename, SNDRV_FILE_CONTROL, card); // 路径/dev/snd/controlC0
fd = snd_open_device(filename, fmode);
fcntl(fd, F_SETFD, FD_CLOEXEC); // 这里设置为FD_CLOEXEC表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递给exec创建的新进程, 如果设置为fcntl(fd, F_SETFD, 0);那么本fd将保持打开状态复制到exec创建的新进程中[luther.gliethttp].
进入内核系统调用
sys_fcntl
do_fcntl
case F_SETFD:
err = 0;
set_close_on_exec(fd, arg & FD_CLOEXEC);
void fastcall set_close_on_exec(unsigned int fd, int flag)
{
struct files_struct *files = current->files;
struct fdtable *fdt;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);
if (flag)
FD_SET(fd, fdt->close_on_exec);
else
FD_CLR(fd, fdt->close_on_exec);
spin_unlock(&files->file_lock);
}
下面是man fcntl看到的对FD_CLOEXEC解释
File descriptor flags
The following commands manipulate the flags associated with a file descriptor. Currently, only one such flag is
defined: FD_CLOEXEC, the close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor will remain open
across an execve(2), otherwise it will be closed.
F_GETFD (void)
Read the file descriptor flags; arg is ignored.
F_SETFD (long)
Set the file descriptor flags to the value specified by arg.
全站熱搜
留言列表