Unix/Linux编程:fork()进程详解

2023-11-11

理论

进程

问: 什么是进程

  • 一个程序的执行称为一个进程,所有的代码都是在进程中执行的。
  • 进程也是操作系统进程资源分片的基本单位

问: 进程是怎么产生的

  • Linux中,除了内核启动进程之外,其他的进程都是由它的父进程产生的。【通过调用fork函数】
    在这里插入图片描述

问:操作系统是怎么识别各个进程的

  • 操心系统通过进程ID(pid)来识别,pid 是进程在操作系统中的唯一标志
  • pid是可以重复使用的,比如说前一个为pid=11的进程死掉了,那么pid=11的这个进程就可以分配给其他进程使用了:当pid达到最大限制时,内核会从头开始查找闲置的pid并使用最先找到的那一个作为新进程的id

操作系统中有一些进程ID是专用的:

  • ID为0的进程叫做调度进程,它是内核的一部分,并不执行任何磁盘上的程序,因此页表成为系统进程
  • ID为1的进程叫做init进程
    • 复制在自举内核之后启动系统,以及读取系统初始化文件,并将系统引导到一个状态。
    • init进程是一个以超级用户权限运行着的普通用户进程,不是系统进程
#include <unistd.h>
/*
* 功能:调用进程的进程ID
*/
__pid_t getpid (void)
/*
* 功能:调用进程的父进程ID
*/
__pid_t getppid (void)
/*
* 功能:调用进程的实际用户ID
*/
__uid_t getuid (void) 
/*
* 功能:调用进程的有效用户ID
*/
__uid_t geteuid (void)
/*
* 功能:调用进程的有效组ID
*/
__gid_t getegid (void)

//上面这些函数都没有出错返回

fork,wait,exec

  • 系统调用 fork()允许一进程(父进程)创建一新进程(子进程)。具体做法是,新的子进程几近于对父进程的翻版:子进程获得父进程的栈、数据段、堆等,但是共享代码段。可将此视为把父进程一分为二,术语 fork 也由此得名。
  • 库函数exit(status)终止一进程,将进程占用的所有资源(内存、文件描述符等)归还内核。参数status为一整形变量,标识进程的退出状态。父进程可以通过系统调用wait()来获取该状态

库函数 exit()位于系统调用_exit()之上。这里只是强调,在调用fork()之后,父、子进程中一般只有一个会通过调用 exit()退出,而另一进程则应使用_exit()终止。

  • 系统调用wait(&status)的目的有二:

    • 其一:如果子进程尚未调用exit()终止,那么wait()会挂起父进程直到子进程终止
    • 其二:子进程的终止状态通过 wait()的 status 参数返回
  • 系统调用 execve(pathname,argv,envp)加载一个新程序(路径名为pathname,参数列表为argv,环境变量列表为envp)到当前进程的内存。这将丢弃现存的程序文本段,并为新程序重新创建栈、数据段以及堆。通常将这一动作称为执行(execing)一个新程序。

下图对 fork()、exit()、wait()以及 exece()之间的相互协同作了总结。(此图勾勒了 shell 执行一条命令所历经的步骤:shell 读取命令,进行各种处理,随之创建子进程以执行该命令,如此循环不已。
在这里插入图片描述

  • 父进程产生子进程使用fork拷贝出来一个父进程的副本,此时只拷贝了父进程的页表,两个进程都读同一块内存
  • 当有进程写的时候使用了写时拷贝机制分配内存,exec函数可以加载一个elf文件去替换父进程,从此父进程和子进程就可以运行不同的程序了
  • fork从父进程返回子进程的pid,从子进程返回0,调用了wait的父进程将会发生阻塞,直到有子进程状态改变,执行成功返回0,错误返回-1
  • exec执行成功则子进程从新的程序开始运行,无返回值,执行失败返回-1

fork

SUSv3 将 vfork()标记为已过时,SUSv4 则进一步将其从规范中删除。所以应尽量避免使用vfork()。

#include<unistd.h>
#include<sys/types.h>
pid_t fork( void);

返回值:

  • pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中。
  • pid_t 是一个叫做“文件描述符”的数据结构中的一个属性:
    • 负值:创建子进程失败。
    • 在父进程中,fork返回新创建子进程的进程ID;
    • 在子进程中,fork返回0;

注意:

  • fork函数被调用一次但返回两次
  • 进程描述符除了记录pid之外,还记录了进程的优先级、状态、虚拟地址范围以及各种访问权限等,还用一个很有用的属性叫做ppid,它是当前进程的父进程的id。

问: 父子进程的关系

  • 子进程是父进程的副本,它将获得父进程数据段、堆、栈等资源的副本, 但是会共享代码段,都从fork函数中返回,箭头表示各自的执行处。当父子进程有一个想要修改数据或者堆栈时,两个进程真正分裂。
  • 每一个副本都是独立的,子进程对于数据他的副本的修改对于其他进程比如父进程、兄弟进程都是不可见的。如果我们相同进程之间相互感知,必须使用进程间的通信手段来通知。

在这里插入图片描述

问: 父子进程的运行时机

  • 由fork创建的新进程被称为子进程, 子进程和父进程会同时运行[但是谁先运行是不一定的,这取决与内核调度]。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

这种不确定性可能会导致所谓“竞争条件(race condition)”的错误

问:fork函数返回的值为什么在父子进程中不同?

程序代码可以通过fork()的返回值来区分父、子进程。

  • 在父进程中,fork()将返回新创建子进程的进程 ID。鉴于父进程可能需要创建,进而追踪多个子进程(通过 wait()或类似方法),这种安排还是很实用的。
  • 而 fork()在子进程中则返回 0。如有必要,子进程可调用 getpid()以获取自身的进程 ID,调用 getppid()以获取父进程 ID

其实就相当于链表,进程形成了链表,父进程的fork函数返回的值指向子进程的进程id, 因为子进程没有子进程,所以其fork函数返回的值为0.

问:父进程和子进程之间的区别

  • fork的返回值不同
  • 进程ID不同
  • 父进程ID不同
  • 子进程的tms_utimetms_stimetms_cutimetms_ustime的值设置为0

问: fork失败的主要原因

  • 系统中已经有了太多的进程,触及允许该系统创建的最大进程数这一系统级上限。
  • 进程数量要么超出了系统针对此真实用户(real user ID)在进程数量上所施加的限制(RLIMIT_NPROC)
  • 子进程不继承父进程设置的文件锁
  • 子进程的未处理闹钟被清除
  • 子进程的未处理信号机设置为NULL

当无法创建子进程时,fork()将返回-1。

问: 父进程正常运行,子进程终止时会发生什么?

  • 当一个进程正常/异常终止时,内核就向其父进程发送SIGCHLD信号。
  • 子进程终止是异步事件(可以在父进程运行的任何时候发送),所以这个信号也是异步信号。父进程可以忽略这个信号(默认),也可以提供一个该信号发生时即被调用执行的函数

总结

要理解fork()的诀窍的关键是,要意识到,完成对其调用后将存在两个进程,而且而且进程都会从fork()的返回值继续执行

这两个进程将执行相同的程序文本段,却各自拥有不同的栈段、数据段以及堆段拷贝。刚开始时,子进程的栈段、堆段、数据段时对父进程内存相应各部分的完全复制。执行fork()之后,每个进程均可以修改各自的栈段、堆段和数据段,而不影响另一进程。

实践

验证 fork函数被调用一次但返回两次

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
 
int main(int argc,char *argv[]){
    pid_t pid=fork();
    if ( pid < 0 ) {
        fprintf(stderr,"错误!");
    } else if( pid == 0 ) {
        printf("子进程空间");
        exit(0);
    } else {
        printf("父进程空间,子进程pid为%d",pid);
    }
    // 可以使用wait或waitpid函数等待子进程的结束并获取结束状态
    exit(0);
}

在这里插入图片描述

子进程和父进程之间不共享数据空间

子进程的栈段、数据段、堆段是父进程的拷贝

#include <fcntl.h>
#include <zconf.h>
#include <stdio.h>
int		globvar = 6;		/* 全局变量在数据段 */
char	buf[] = "a write to stdout\n";

int main(void)
{
    int		var;		/*自动变量在栈段 */
    pid_t	pid;

    var = 88;
    if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1){
        printf("write error");
        _exit(0);
    }

    printf("before fork\n");	
    if ((pid = fork()) < 0) {
        printf("fork error");
        _exit(0);
    } else if (pid == 0) {		/* 子进程(子进程运行的代码段从这里开始,这行之前的不执行)*/
        globvar++;				/* 修改全局变量和局部变量 */
        var++;
    } else {
        sleep(2);				/* 父进程:睡觉2s以便子进程先运行 */
    }

    printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);
    _exit(0);
}

在这里插入图片描述

从上面可以看出:

  • 父子进程共享代码段,但是不共享数据段:(全部变量和栈上的局部变量是独立的,在子进程中修改不会影响父进程,在父进程中修改也不会影响子进程)

问: 如果将write改成待缓冲的写入,会输出什么?

问:strlen与sizeof的区别

  • strlen计算不包含null字节的字符串长度, sizeof计算包含null字节的字符串长度
  • strlen需要一次函数调用,sizeof在编译时计算缓冲区长度

父子进程间的文件共享

执行fork()时,子进程会获得父进程所有文件描述符的副本。这些副本的创建方式类似于dup(),这也意味着父、子进程中对应的描述符均指向相同的打开文件句柄。打开文件句柄包含有当前文件偏移量(由 read()、write()和 lseek()修改)以及文件状态标志(由 open()设置,通过 fcntl()的 F_SETFL 操作改变)。一个打开文件的这些属性因之而在父子进程间实现了共享。举例来说,如果子进程更新了文件偏移量,那么这种改变也会影响到父进程中相应的描述符

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <zconf.h>

int
main(int argc, char *argv[])
{
    int fd, flags;
    char tmplate[] = "/tmp/testXXXXXX";

    setbuf(stdout, NULL);                   /* Disable buffering of stdout */

    /* Open a temporary file, set its file offset to some arbitrary value,
       and change the setting of one of the open file status flags. */

    fd = mkstemp(tmplate);
    if (fd == -1){
        perror("mkstemp");
        exit(EXIT_FAILURE);
    }


    printf("File offset before fork(): %lld\n",
           (long long) lseek(fd, 0, SEEK_CUR));

    flags = fcntl(fd, F_GETFL);
    if (flags == -1){
        perror("fcntl F_GETFL");
        exit(EXIT_FAILURE);
    }

    printf("O_APPEND flag before fork() is: %s\n",
           (flags & O_APPEND) ? "on" : "off");

    switch (fork()) {
        case -1:
            perror("fork");
            exit(EXIT_FAILURE);


        case 0:     /* Child: change file offset and status flags */
            if (lseek(fd, 1000, SEEK_SET) == -1){
                perror("lseek  SEEK_SET");
                exit(EXIT_FAILURE);
            }

            flags = fcntl(fd, F_GETFL);         /* Fetch current flags */
            if (flags == -1){
                perror("fcntl --- F_GETFL");
                exit(EXIT_FAILURE);
            }
            flags |= O_APPEND;                  /* Turn O_APPEND on */
            if (fcntl(fd, F_SETFL, flags) == -1){
                perror("fcntl --- F_SETFL");
                exit(EXIT_FAILURE);
            }
            _exit(EXIT_SUCCESS);

        default:    /* Parent: can see file changes made by child */
            if (wait(NULL) == -1){
                perror("wait");
                exit(EXIT_FAILURE);
            }         /* Wait for child exit */
            printf("Child has exited\n");

            printf("File offset in parent: %lld\n",
                   (long long) lseek(fd, 0, SEEK_CUR));

            flags = fcntl(fd, F_GETFL);
            if (flags == -1){
                perror("fcntl F_GETFL ");
                exit(EXIT_FAILURE);
            }
            printf("O_APPEND flag in parent is: %s\n",
                   (flags & O_APPEND) ? "on" : "off");
            exit(EXIT_SUCCESS);
    }
}

如果不需要这种对文件描述符的共享方式,那么在设计应用程序时,应于 fork()调用后注意两点:其一,令父、子进程使用不同的文件描述符;其二,各自立即关闭不再使用的描述符(亦即那些经由其他进程使用的描述符)。

在这里插入图片描述

fork的内存语义

从概念上来说,可以将fork()认做是对父进程程序段、数据段、堆段以及栈段创建拷贝。早期的Unix实现中,此类复制确实是如此:将父进程内存拷贝至交换空间,以此创建新进程映像,而在父进程保持自身内存的同时,将换出映像置为子进程。不过,真要是简单地将父进程虚拟内存页拷贝到新的子进程,那就太浪费了。原因有很多,其中之一是:fork()之后尝尝伴随着exec(),这会用新程序替换进程的代码段,并重新初始化其数据段、堆段和栈段。大部分现代Unix实现中采用两种技术来避免这种浪费:

  • 内核将每一进程的代码段标记为自读,从而使进程无法修改自身代码。这样,父子进程可以共享同一代码段。系统调用fork()在为子进程创建代码段时,其所构建的一系列进程级页表项均指向与父进程相同的物理内存页帧
  • 对于父进程数据段、堆段和栈段中的各页,内核采用写时复制(copy-on-write)技术来处理。最初,内核做了一些设置,令这些段的页表项指向与父进程相同的物理内存页,并将这些页面自身标记为只读。调用 fork()之后,内核会捕获所有父进程或子进程针对这些页面的修改企图,并为将要修改的(about-to-be-modified)页面创建拷贝。系统将新的页面拷贝分配给遭内核捕获的进程,还会对子进程的相应页表项做适当调整。从这一刻起,父、子进程可以分别修改各自的页拷贝,不再相互影响

在这里插入图片描述

同步信号以规避 fork()之后的竞争条件

调用 fork()后,无法确定父、子进程间谁将率先访问 CPU。不应对 fork()之后执行父、子进程的特定顺序做任何假设。若确需保证某一特定执行顺序,则必须采用某种同步技术,比如信号量(semaphore)、文件锁(file lock)以及进程间经由管道(pipe)的消息发送等。接下来我们使用同步信号以规避 fork()之后的竞争条件

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <zconf.h>
#include <time.h>
#include <errno.h>
char *currTime(const char *format)
{
#define BUF_SIZE 1000
    static char buf[BUF_SIZE];  /* Nonreentrant */
    time_t t;
    size_t s;
    struct tm *tm;

    t = time(NULL);
    tm = localtime(&t);
    if (tm == NULL)
        return NULL;

    s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);

    return (s == 0) ? NULL : buf;
}

#define SYNC_SIG SIGUSR1                /* Synchronization signal */
static void             /* Signal handler - does nothing but return */
handler(int sig)
{
}
int main(int argc, char *argv[])
{
    pid_t childPid;
    sigset_t blockMask, origMask, emptyMask;
    struct sigaction sa;

    setbuf(stdout, NULL);               /* Disable buffering of stdout */

    sigemptyset(&blockMask);
    sigaddset(&blockMask, SYNC_SIG);    /* Block signal */
    if (sigprocmask(SIG_BLOCK, &blockMask, &origMask) == -1){
        perror("sigprocmask");
        exit(EXIT_FAILURE);
    }


    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = handler;
    if (sigaction(SYNC_SIG, &sa, NULL) == -1){
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    switch (childPid = fork()) {
        case -1:
            perror("fork");
            exit(EXIT_FAILURE);

        case 0: /* Child */

            /* Child does some required action here... */

            printf("[%s %ld] Child started - doing some work\n",
                   currTime("%T"), (long) getpid());
            sleep(2);               /* Simulate time spent doing some work */

            /* And then signals parent that it's done */

            printf("[%s %ld] Child about to signal parent\n", currTime("%T"), (long) getpid());
            if (kill(getppid(), SYNC_SIG) == -1){
                perror("kill");
                exit(EXIT_FAILURE);
            }

            /* Now child can do other things... */

            _exit(EXIT_SUCCESS);

        default: /* Parent */

            /* Parent may do some work here, and then waits for child to
               complete the required action */

            printf("[%s %ld] Parent about to wait for signal\n",
                   currTime("%T"), (long) getpid());
            sigemptyset(&emptyMask);
            if (sigsuspend(&emptyMask) == -1 && errno != EINTR){
                perror("sigsuspend");
                exit(EXIT_FAILURE);
            }

            printf("[%s %ld] Parent got signal\n", currTime("%T"), (long) getpid());

            /* If required, return signal mask to its original state */

            if (sigprocmask(SIG_SETMASK, &origMask, NULL) == -1){
                perror("sigprocmask");
                exit(EXIT_FAILURE);
            }


            /* Parent carries on to do other things... */

            exit(EXIT_SUCCESS);
    }
}

在这里插入图片描述

fork


#include <fcntl.h>
#include <zconf.h>
#include <stdio.h>
#include <sys/wait.h>

int main(void)
{
    pid_t	pid;

    if ((pid = fork()) < 0) {
        printf("fork error");
        _exit(0);

    } else if (pid == 0) {		//first child
        printf(" first child from main fork :  curr pid = %ld, parent pid = %ld\n", (long)getpid(),  (long)getppid());
        if ((pid = fork()) < 0){
            printf("fork error");
            _exit(0);
        }else if (pid > 0){
            printf(" first child from main fork :  curr pid = %ld, parent pid = %ld\n", (long)getpid(),  (long)getppid());
            // sleep(10);
            _exit(0);
        }

        sleep(2);
        printf("second child from first child:  pid = %d, getpid = %ld, parent pid(because first child had exit,  parent changed init proceess, so parent child = 1) = %ld\n",pid, (long)getpid(),  (long)getppid());
        _exit(0);
    }

    printf("  main fork :  curr pid = %ld, pid = %ld\n", (long)getpid(),  (long)pid);
    if  (waitpid(pid, NULL, 0) != pid){ /* wait for first child : waitpid会暂时停止目前进程的执行,直到有信号来到或子进程结束。 */
        printf("waitpid error");
        _exit(0);
    }

    printf("main exit\n");
    _exit(0);
}

在这里插入图片描述

  • 当子进程的父进程exit之后,当前子进程的父进程就变成了init进程
    • 在一个进程终止时,内核会逐个检查所有的活动进行,以判断它是否是正在终止进程的子进程,如果是,就将进程的父进程ID更为为1
      在这里插入图片描述

解决竞态

#include "apue.h"

static void charatatime(char *);

int main(void)
{
    pid_t	pid;

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        charatatime("output from child child child child child child child child child\n");
    } else {
        charatatime("output from parent parent parent parent parent parent parent parent parent\n");
    }
    exit(0);
}

static void charatatime(char *str)
{
    char	*ptr;
    int		c;

    setbuf(stdout, NULL);			/* set unbuffered: 设置为无缓冲之后,每次字符输出都会调用一次write */
    for (ptr = str; (c = *ptr++) != 0; )
        putc(c, stdout);
}

在这里插入图片描述

#include "apue.h"

static void charatatime(char *);

int
main(void)
{
    pid_t	pid;

    TELL_WAIT(); // 告知需要等待

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        WAIT_PARENT();		/* 等待父进程结束之后再运行 */
        charatatime("output from child child child child child child child child child\n");
    } else {
        charatatime("output from parent parent parent parent parent parent parent parent parent\n");
        TELL_CHILD(pid);  // 告知子进程已经退出了
    }
    exit(0);
}

static void charatatime(char *str)
{
    char	*ptr;
    int		c;

    setbuf(stdout, NULL);			/* set unbuffered */
    for (ptr = str; (c = *ptr++) != 0; )
        putc(c, stdout);
}

在这里插入图片描述

#include "apue.h"

static void charatatime(char *);

int main(void)
{
    pid_t	pid;

    TELL_WAIT();

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        charatatime("output from child child child child child child child child child\n");
        TELL_PARENT(getppid());
    } else {
        WAIT_CHILD();
        charatatime("output from parent parent parent parent parent parent parent parent parent\n");
        TELL_CHILD(pid);
    }
    exit(0);
}

static void charatatime(char *str)
{
    char	*ptr;
    int		c;

    setbuf(stdout, NULL);			/* set unbuffered */
    for (ptr = str; (c = *ptr++) != 0; )
        putc(c, stdout);
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unix/Linux编程:fork()进程详解 的相关文章

  • 使用 Python 将阿拉伯语或任何从右到左书写系统的字符串打印到 Linux 终端

    非常简单的例子是 city print city 我期望输出是 但实际上输出是相反的字符串 字母看起来有点不同 因为它们有开始 中间和结束形式 我无法将其粘贴到此处 因为复制粘贴会再次更正字符串的顺序 如何在 Linux 终端上正确打印阿拉
  • 相当于 UNIX diff 和 patch 的本机 PowerShell

    我需要潜在地修补文件作为脚本的一部分 为了使脚本所做的事情更具可读性 我想以类似于 UNIX diff 和 patch 方法的方式来实现它 在标准 UNIX 系统上 diff 可以生成特殊格式的文本文件 表示两个文件之间的差异 这可以与要修
  • 使用 .htaccess 启用 PHP 短标签

    我在自己的 Centos 服务器上设置了 Apache 并具有多个虚拟 Web 服务器 并且我希望仅为位于以下位置的其中一个 Web 服务器启用 PHP 短标记 var www ostickets html 我可以通过添加成功启用短标签sh
  • 重新链接匿名(未链接但打开)文件

    在 Unix 中 可以创建匿名文件的句柄 例如 使用 creat 创建并打开它 然后使用 unlink 删除目录链接 留下一个带有 inode 和存储的文件 但没有可能的方法重新打开它 此类文件通常用作临时文件 通常这就是 tmpfile
  • Unix shell脚本找出脚本文件所在的目录?

    基本上我需要使用与 shell 脚本文件位置相关的路径运行脚本 如何将当前目录更改为与脚本文件所在的目录相同 在 Bash 中 你应该得到你需要的东西 如下所示 usr bin env bash BASEDIR dirname 0 echo
  • sudo pip install python-Levenshtein 失败,错误代码 1

    我正在尝试在 Linux 上安装 python Levenshtein 库 但每当我尝试通过以下方式安装它时 sudo pip install python Levenshtein 我收到此错误 命令 usr bin python c 导入
  • PHP 日志文件颜色

    我正在编写一个 PHP 日志文件类 但我想为写入文件的行添加颜色 我遇到的问题是颜色也会改变终端的颜色 我想要实现的是仅更改写入日志文件的行的颜色 class logClass extends Singleton private funct
  • 在Linux中创建可执行文件

    我计划做的一件事是编写 非常简单的 Perl 脚本 并且我希望能够在不从终端显式调用 Perl 的情况下运行它们 我明白 要做到这一点 我需要授予他们执行权限 使用 chmod 执行此操作非常简单 但它似乎也是一个稍微费力的额外步骤 我想要
  • ARM 的内核 Oops 页面错误错误代码

    Oops 之后的错误代码给出了有关 ARM EX 中的恐慌的信息 Oops 17 1 PREEMPT SMP在这种情况下 17 给出了信息 在 x86 中它代表 bit 0 0 no page found 1 protection faul
  • 即使使用 rvm pkg install zlib 后也无法加载此类文件 -- zlib

    我使用 rvm 安装了 zlib 包和 ruby 1 9 3 但是每当我尝试安装时 它说宝石cannot load such file zlib 我用来安装的命令是 rvm install 1 9 3 rvm pkg install zli
  • Linux Shellcode“你好,世界!”

    我有以下可用的 NASM 代码 global start section text start mov eax 0x4 mov ebx 0x1 mov ecx message mov edx 0xF int 0x80 mov eax 0x1
  • 如果目录不存在,有没有办法让 mv 创建要移动到的目录?

    因此 如果我在主目录中并且想将 foo c 移动到 bar baz foo c 但这些目录不存在 是否有某种方法可以自动创建这些目录 以便你只需要输入 mv foo c bar baz 一切都会顺利吗 似乎您可以将 mv 别名为一个简单的
  • C# - OPC-UA 服务器应用程序尚未在 Linux 计算机中创建 PKI 证书

    当我跑步时OPC UA serverWindows 机器中的 C 应用程序 然后 OPC UA 服务器已创建证书路径C ProgramData OPC Foundation pki own 并在此路径中生成一些证书 但是当我在中安装 OPC
  • 在Linux中将日期附加到文件名

    我想在文件名旁边添加日期 somefile txt 例如 somefile 25 11 2009 txt 或 somefile 25Nov2009 txt 或任何类似的内容 也许脚本或终端窗口中的某些命令可以执行 我正在使用Linux Ub
  • 在 Bash 中拆分 csv 文件中的列

    我想从 csv 文件的第二列中提取值并将提取的值存储在新列中 我的数据集示例 page name post id page id A 86680728811 272953252761568 86680728811 A 86680728811
  • bash "&" 不打印 "[1]+ Done "

    我在 bashrc 中调用一个脚本来打印打开终端时收到的新消息数 我希望该调用在访问网络时是非阻塞的 有时需要几秒钟 这意味着我无法使用终端直到完成 但是如果我输入 mailcheck 在我的 bashrc 中 它工作正常 但然后打印一个空
  • SVN 不断提示我输入密码并拒绝缓存我的凭据

    环境 Eclipse Indigo Ubuntu 11 04 Subclipse 1 6 SVN 客户端 Subclipse RabbitVCS 我通过 svn ssh 连接 我的网址如下所示 svn ssh 我的名字 我的域名 路径 我可
  • 为什么 proc/ID/maps 有多个共享库条目

    我正在查看嵌入式Linux下的proc ID maps 我注意到一些共享库在进程的内存映射中出现了几次 为什么会这样呢 40094000 400d9000 r xp 00000000 b3 09 723 system lib libc so
  • Laravel 内存问题?

    各位 我在 DO 服务器上遇到这样的问题 我已经尝试了一切 整个网站在使用 Homestead 的 Linux 服务器上 100 正常工作 但上传后 它只能工作一次 在重新加载或刷新页面后会多次下降 我尝试增加 apache 服务器的内存
  • 我如何知道用户在使用 ncurses (Linux) 的控制台中按下了 ESC 键?

    I have a problem in detecting whether I just got a plain ESC key just code 27 or whether it was another special key such

随机推荐

  • 计算机学习顺序及其课程连接

    https www bilibili com video BV1Qt411J7mo p 1
  • MySQL数据存储原理一

    执行计划 id sql比较复杂的话 id列值会有好几个 它表示具体sql语句要执行的顺序 type 表示访问数据或进行查询的时候 所对应的类型是什么 效率优先级由低到高 all gt index gt range gt index ref
  • Python知识点讲解之Python冒号的使用

    本文要讲解Python的知识点是Python冒号 我们都知道 逗号 分隔各个维度 表示各个维度内的切片 只有 表示取这个维度的全部值 那么下面小编有一个例子来详细分析下Python冒号的使用 例子 a 1 2 3 4 2 3 4 5 5 6
  • 【HTML】多行文本框随字数增加,高度增加

    转载地址 https blog csdn net xj 9264 article details 85611526
  • .Net Core基础之读取配置文件

    在应用程序开发中 配置文件是主要存储系统的初始配置信息 配置文件的读取虽然属于基础内容却又经常用到 所以百丈高楼平地起 学习 Net Core 就从学习配置文件开始 在 net framework时代 配置文件主要是xml格式 web co
  • 创建型模式2——工厂模式(简单工厂、工厂方法、抽象工厂)

    简单工厂 模式动机 意图 定义一个创建对象的接口 让其子类自己决定实例化哪一个工厂类 工厂模式使其创建过程延迟到子类进行 主要解决 主要解决接口选择的问题 简单工厂模式的要点在于 当你需要什么 只需要传入一个正确的参数 就可以获取你所需要的
  • OpenGL ES加载纹理

    iOS OpenGL ES加载纹理 GLKit 1 准备工作 创建UIViewController文件并继承GLKViewController 遵守协议GLKViewDelegate 实现协议方法 void glkView GLKView
  • [Vue warn] Failed to resolve component:报错问题。

    在一个vue3项目中 引入一个自定义组件时出现了 Vue warn Failed to resolve component 组件名 这样的警告 引入的组件没有出现在页面中 网上查了半天没找到原因 后来将vue3中的defineCompone
  • cs231n_反向传播求导篇

    我们已经知道基本的标量除以矩阵 或向量 矩阵 或向量 除以标量 以及稍微复杂一点的向量除向量 1 1 1 行向量除以向量除以矩阵 矩阵除以向量的方法 那么矩阵除以矩阵呢 例如 XN DWD C fN C X N
  • 用python+pytest框架写UI自动化

    使用 Python 编写 UI 自动化测试通常需要使用 Pytest 测试框架 下面是编写 Python Pytest UI 自动化测试的一般步骤 安装 Python Pytest 和相关的库 例如 Selenium WebDriver 和
  • WM_CHAR 获取键盘按下的字符

    DEMO3 11 CPP WM CHAR demo INCLUDES define WIN32 LEAN AND MEAN just say no to MFC include
  • QT-固定全局文字大小

    问题描述 W10 将缩放与布局设置为 gt 100 时 QT的文字会放大超过原来的文字框 解决 使用这种方法是让Windows来控制缩放 而不是Qt 添加一个资源文件qt conf 内容为 Platforms WindowsArgument
  • linux 时钟漂移,Redis 实现分布式锁之Redlock 算法浅析

    保证分布式锁有效的三个属性 Safety Properties 安全性 此处也就是互斥性 任意时刻只能有一个客户端可以持有锁 Liveness Property A 无死锁 即使持有锁的客户端崩溃或被分区 也可以获得锁 Liveness P
  • pandas中groupby函数中参数ax_index和group_keys的区别

    前言 笔者在学习pandas中groupby函数时 发现ax index True False和group key True False这两个参数相近又有所不同 特写出此文供大家分享 一 首先创建一个DataFrame df pd Data
  • thymeleaf模板报红

    问题 解决 忽略所有警告或错误 1 或者忽略Thymeleaf有关警告或错误 2 2 取消这个勾 关闭IDEA对于thymeleaf的数据验证选项
  • BP神经网络的详细推导

    文章目录 概述 一 神经元模型 二 感知机与多层网络 三 误差逆传播算法 四 全局最小与局部最小 五 BP算法的改进 1 引入动量法 2 尺度变换法 3 自适应学习率调整法 六 BP神经网络的训练 1 产生数据样本集 2 确定网络的类型和结
  • QMap倒序遍历

    for QList
  • 中英文期刊卷号和期号

    一 中文期刊 国内期刊为了给期刊排序方便查询 都按照时间分卷和期 卷是期之上的时间分类 卷是从创刊开始按照年度排序的编号 期是这一年中按时间排序的编号 比如2017年3月发表的论文 按照卷号期号排可能就是 第23卷第3期 国内对期刊卷号期号
  • 【简述】VSCode使用ssh连接linux服务器并安装使用jupyter notebook/Anaconda/pytorch

    1 通过管理员获得该Unbutu服务器的IP 账号 密码 2 打开VSCode 安装扩展Remote SSH 3 在VsCode左侧边栏点击Remote Explorer使用该扩展 新建Remote 输入IP 账号并连接 4 在上方弹出窗口
  • Unix/Linux编程:fork()进程详解

    文章目录 理论 进程 fork wait exec fork 实践 验证 fork函数被调用一次但返回两次 子进程和父进程之间不共享数据空间 父子进程间的文件共享 fork的内存语义 同步信号以规避 fork 之后的竞争条件 fork 解决