【Linux】如何在进程间加锁(实现互斥)

2023-11-15


前言

【Linux】初识进程间通信:建议先读完这篇。

进程之间如何加锁,今天我们需要实现一个售票系统,我们需要对同一个num变量++。
以往我们写过类似的代码,只需要用pthread_mutex_t 这把锁就可以实现一个进程内多个线程互斥。但是我们这里改变需求,假设是你和你的同学需要抢一张票,你们在不同的主机的相同进程抢票,那么要怎么做呢?

mmap 配合 pthread_mutex_t


pthread_mutex_t要使用,需要pthread_mutexattr_t的帮助,将原先的线程锁换为进程锁。
mmap 这个函数功能是在共享区当中申请一块内存空间。
是将用户空间的一段内存区域映射到内核空间,映射成功之后,用户对这段内存区域的修改可以直接反映到内核空间。

先让多个进程能够看到一个num


那么我们可以定义一个结构体,mt结构体,其中num标识总票数,mutex是互斥锁,mutexattr是锁的属性。

void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);

struct mt
{
    int num; //多个进程的票数
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
};

mt 对齐,结构体对象对齐时按照其最大的对齐数进行对齐。 这里推荐把mutexattr写到mutex前面,因为这样总体消耗的内存最小。(56 -》 48)
在共享内存开辟mt空间大小,返回指针给两个进程。两个进程得到的指针值相同。

由于此处是父子进程,不需要依赖文件描述符,fd填写为-1,MAP_SHARED|MAP_ANON的组合标识需要保存的文件并且是匿名的mmap,仅在进程间使用。PROT_READ|PROT_WRITE标识这块共享区的读写权限。

匿名的mmap:

  • 适合父子进程使用
mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);

有名的mmap:

  • 打开一个文件,获取文件描述符
  • ftruncate进行设置文件大小,将文件大小设置成struct mt的大小,若原先文件大小超过sizeof(*mm),则会被截断为sizeof(*mm),若文件大小小于,则会扩到sizeof(*mm)
  • mmap进行设置,将共享内存当中开辟一块内存空间。
  • 关闭打开的文件描述符
  • 删除文件

注意 :MAP_SHARED不进行设置的话后续会报段错误。
这引入我们对flags变量的思考:

  • 若是设置成0,那么由于内核暂不支持,所以返回值会是MAP_FAILED,(void*)-1,也就是0xffffffffffffffff,那么后续对返回值做操作,都会出现段错误。
    在这里插入图片描述在这里插入图片描述
  • 若是设置为MAP_SHARED,两个进程是能够互斥访问的。在这里插入图片描述
  • 若是设置成MAP_PRIVATE,明显加的不是一个变量,说明父子发生写时拷贝,操作系统会在共享区开辟一块同属性的内存,让父进程或者子进程去读写。达不到进程同步。
    在这里插入图片描述
 int fd = open("mt_test",O_CREAT|O_RDWR,0777);// 打开文件,获得一个fd
    if( fd == -1 ) 
    {
        perror("open file:"); 
        exit(1); 
    }
int n = ftruncate(fd,sizeof(*mm)); // 截取fd的部分
    mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    close(fd);
    unlink("mt_test"); // 删除文件

两种创建mmap的方式是一种万金油,任意进程都可以使用,另一种适合父子通信。

多个进程互斥访问


pthread_mutex默认是线程间通信加的锁,要是想用在进程之间,需要pthread_mutexattr_t *restrict attr 填充这个字段,在属性字段当中修改为PTHREAD_PROCESS_SHARED标识进程间共享。

 pthread_mutexattr_init(&mm->mutexattr);        
  // 初始化 mutex 属性
 pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);               
 // 修改属性为进程间共享
 
pthread_mutex_init(&mm->mutex,&mm->mutexattr);      
// 初始化一把 mutex 锁

上述工作过后,已经在共享区创建了一个结构体,并且父进程已经接受了指向共享区的指针,fork创建子进程之后父子进程都可以挂接在共享区上面。

在这里插入图片描述

接下来,父子实现互斥访问就简单了。使用之前只需要先用mm->mutex进行加锁即可。同理,若是放着的是原子变量,自旋锁,读写锁,也都是可以使用的。

pid = fork();
    if( pid == 0 )          // 子进程
    {
        for( i=0; i<10;i++ )
        {
            pthread_mutex_lock(&mm->mutex);
            (mm->num)++;
            printf("-child--------------num++    %d\n",mm->num);
            pthread_mutex_unlock(&mm->mutex);
            sleep(1);
        }
    
    }
    else 
    {
        for( i=0;i<10;i++)
        {
            sleep(1); 
            pthread_mutex_lock(&mm->mutex);
            mm->num += 2;
            printf("--------parent------num+=2   %d\n",mm->num);
            pthread_mutex_unlock(&mm->mutex);
        
        }
        wait(NULL);
 
    }
    pthread_mutexattr_destroy(&mm->mutexattr);  // 销毁 mutex 属性对象
    pthread_mutex_destroy(&mm->mutex);          // 销毁 mutex 锁

具体代码

/*
    互斥量 实现 多进程 之间的同步 
*/
 
#include<unistd.h>
#include<sys/mman.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
 
struct mt
{
    int num;
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
};
 
// 通过父子进程对同一个变量 mm->num 进行++ ,父进程一次+2 ,子进程一次 +1
int main(void)
{
    
    int i;
    struct mt* mm;
 
    pid_t pid;
    

    // 创建映射区文件
    int fd = open("mt_test",O_CREAT|O_RDWR,0777);
    if( fd == -1 ) 
    {
        perror("open file:"); 
        exit(1); 
    }
    ftruncate(fd,sizeof(*mm));
    mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED ,fd,0);
    close(fd);

    unlink("mt_test");


    //就是这个mt结构体,当我们创建mt
    // 建立映射区 MAP_ANON匿名映射
    //mm = (mt*)mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
    //    printf("-------before memset------\n");
    //memset(mm,0x00,sizeof(*mm));
    //   printf("-------after memset------\n");
 
    pthread_mutexattr_init(&mm->mutexattr);         // 初始化 mutex 属性
    pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);               // 修改属性为进程间共享
 
    pthread_mutex_init(&mm->mutex,&mm->mutexattr);      // 初始化一把 mutex 锁
        
    
    pid = fork();
    if( pid == 0 )          // 子进程
    {
        for( i=0; i<10;i++ )
        {
            pthread_mutex_lock(&mm->mutex);
            (mm->num)++;
            printf("-child--------------num++    %d\n",mm->num);
            pthread_mutex_unlock(&mm->mutex);
            sleep(1);
        }
    
    }
    else 
    {
        for( i=0;i<10;i++)
        {
            sleep(1); 
            pthread_mutex_lock(&mm->mutex);
            mm->num += 2;
            printf("--------parent------num+=2   %d\n",mm->num);
            pthread_mutex_unlock(&mm->mutex);
        
        }
        wait(NULL);
 
    }
    pthread_mutexattr_destroy(&mm->mutexattr);  // 销毁 mutex 属性对象
    pthread_mutex_destroy(&mm->mutex);          // 销毁 mutex 锁
 
    return 0;
}

采用共享内存配合信号量


信号量我们使用System V的这套接口。在说到具体的实现方案,不得不说分辨一下System V的信号量,Posix V的信号量,Posix还分为有名和无名的信号量。

System V的特点是同时能操作多个信号量,接口使用难度比Posix要复杂,移植性不太好。
Posix 是
解释一下为什么Posix 无名信号量适合线程同步,因为sem_init是将信号集sem_t进行初始化,而若要实现进程间的互斥,需要多个进程看到sem_t,就需要借助其他手段让其他进程看到sem_t。

System V的信号量是随内核的。
Posix有名信号灯的值是随内核持续的。
Posix无名信号灯的持续性却是不定的,因为他是基于内存的,如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它也会消失。
总结:

  • 1、System V的信号量一般用于进程同步, 且是内核持续的, api为:semget、semctl、semop
  • 2、Posix的有名信号量一般用于进程同步, 有名信号量是内核持续的. 有名信号量的api为:sem_open、sem_close、sem_unlink
  • 3、Posix的无名信号量一般用于线程同步, 无名信号量是进程持续的, 无名信号量的api为:sem_init、sem_destroy

先介绍一下接口:

semget

int semget(key_t key, int nsems, int semflg);

  • key通常可以由ftok创建
  • nsems标识需要创建多少个信号量,我通常只需要操纵一个,所以这里填写1
  • semflg标识信号量的权限,服务端通常IPC_CREAT | IPC_EXCL都带上,标识创建一个新的IPC资源,客户端可以填0。
  • 返回值就是semid,标识内核维护的IPC资源的下标(操作系统是用数组维护IPC资源的ipc_perm),出错返回-1.
    在这里插入图片描述

在这里插入图片描述

semctl

int semctl(int semid, int semnum, int cmd, …);

  • semid:就是semget获得的返回值
  • semnum:semget传参的时候nsems,这里要操作的就是0~nsems-1的信号集
  • cmd:标识对内核维护的信号量做什么操作,如:SETVAL,IPC_RMID,注意初始化值的时候应用SETVAL并非IPC_SET,IPC_SET是指用户层创建了一个结构体,用这个结构体的字段重新多创建一个IPC资源(我的理解,仅供参考)
  • …:这里的可变参数需要我们传多一个用户层自定义的结构体,这里参考内核的union semun,我们定义了semun后,设置val等同于设置该信号量为几元信号量,犹如设置1,那么他就可以为0或者1,也称之为二元信号量搭配SETVAL使用。这里我们只是实现互斥,所以此处填写1。
  • 返回值:失败返回-1

下方为内核,实际我们编写的时候可以只有int val这个字段。

union semun {
 int     val;   
  /* Value for SETVAL */
struct semid_ds *buf;    
/* Buffer for IPC_STAT, IPC_SET */
unsigned short  *array;  
/* Array for GETALL, SETALL */
 struct seminfo  *__buf;  
 /* Buffer for IPC_INFO
                                           (Linux-specific) */
  };

semop

int semop(int semid, struct sembuf *sops, unsigned nsops);

  • semid:semget返回值
  • sops:sem_num 标识需要操作的信号集具体是哪个,如果只创建了一个,那么这里是0;sem_op标识进行何种操作,此处填写-1标识获取当前信号量,填写1标识归还信号量;sem_flg表示以何种方式进行semop操作,若是阻塞,则填0,还有类似IPC_NOWAIT,SEM_UNDO(通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时)等字段
  • nsops标识操作第几块IPC资源:这里若semget填写的是1,这里也填写1,标识操作第一块信号量,这里就不是从0开始了。
  • 返回值:-1标识错误
struct sembuf{
unsigned short sem_num;  /* semaphore number */
short          sem_op;   /* semaphore operation */
short          sem_flg;  /* operation flags */
}

核心逻辑


父进程创建共享内存,创建信号量,由信号量标识当前资源是否被使用,由共享内存将真正的数据(ticket)放到共享区当中。当父子任意进程访问ticket,都需要semop获得信号量,这样就能保证临界资源受到保护了。

需要注意的是,可以将信号量,共享内存在fork之前创建,之后调用接口不会引起写时拷贝。

--*pticket;不要写成 *pticket --; Linux下会出现问题!!!!

  • semop(semid, &sb, 1) == -1 && EAGAIN == errno可以用来标识semctl删除信号量,op操作自然会失败,errno为EAGAIN。semop(semid, &sb, 1) == -1 也可以作为判断。
  • 最终票数为0,父子进程都调用semctl删除共享资源,只有一方会调用成功,当父子进程都semdt取消挂接后该资源便会释放。共享内存和信号量都需要调用对应接口,否则会造成资源泄露,都是随内核的。
#include <iostream>
using namespace std;
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>

union semun
{
    int val;               /* value for SETVAL */
    struct semid_ds *buf;  /* buffer for IPC_STAT, IPC_SET */
    unsigned short *array; /* array for GETALL, SETALL */
    struct seminfo *__buf; /* buffer for IPC_INFO */
};
int main()
{
    int key = ftok("sem_sys.cc", 9000);
    int shm_key = ftok(".", 8080);
    int shmid = shmget(shm_key, 4096, IPC_CREAT | IPC_EXCL | 0644);
    if (shmid < 0)
    {
        perror("shmget");
        return 1;
    }
    int *pticket = (int *)shmat(shmid, nullptr, 0);

    *pticket = 1000; // 1000张票

    int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0644);
 
    if (semid < 0)
    {
        perror("semget");
        return 2;
    }
    semun se;
    se.val = 1;//标识单个信号量的资源大小,互斥填1,这里就会两态。
    // SETVAL   IPC_SET
    int res = semctl(semid, 0, SETVAL, se);
    if (res == -1)
    {
        perror("semctl");
        return 4;
    }
 
    pid_t id = fork();
    if (id == 0)
    {
        while (1)
        {
            // child
            struct sembuf sb; // P
            sb.sem_flg = 0;   //阻塞拿
            sb.sem_num = 0;   //拿第一个
            sb.sem_op = -1;   //获取信号量

            if (semop(semid, &sb, 1) == -1 && EAGAIN == errno)
                break;
            // cout << "child " << *pticket_child << endl;
            //获得了这个信号量
            if (*pticket <= 0)
            {
                semctl(semid, 0, IPC_RMID);
                break;
            }
            --*pticket;
            cout << "child get ticket: " << *pticket << endl;

            usleep(1);

            // V
            struct sembuf sb2;
            sb2.sem_flg = 0;
            sb2.sem_num = 0;
            sb2.sem_op = 1;//释放信号量
            semop(semid, &sb2, 1);
        }
        shmdt(pticket);
    }
    else
    {
        while (1)
        {
            // father
            struct sembuf sb; // P
            sb.sem_flg = 0;
            sb.sem_num = 0;
            sb.sem_op = -1;

            if (semop(semid, &sb, 1) == -1 && EAGAIN == errno)
                break;

            //获得了这个信号量
            // cout << "father " << *pticket_father << " :" << &pticket_father << endl;
            if (*pticket <= 0)
            {
                semctl(semid, 0, IPC_RMID);
                break;
            }
            --*pticket;
            cout << "father get ticket: " << *pticket << " :" << &pticket << endl;

            usleep(1);

            // V
            struct sembuf sb2;
            sb2.sem_flg = 0;
            sb2.sem_num = 0;
            sb2.sem_op = 1;
            semop(semid, &sb2, 1);
        }
        shmdt(pticket);
        shmctl(shmid, IPC_RMID, nullptr);
    }
    return 0;
}

一开始担心会发生写时拷贝写的版本,让子进程让出时间片给父进程创建信号量和共享内存,子进程后进行挂接。

#include <iostream>
using namespace std;
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>

union semun
{
    int val;               /* value for SETVAL */
    struct semid_ds *buf;  /* buffer for IPC_STAT, IPC_SET */
    unsigned short *array; /* array for GETALL, SETALL */
    struct seminfo *__buf; /* buffer for IPC_INFO */
};
int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        usleep(100); //让父进程先启动
        int key = ftok("sem_sys.cc", 9000);
        int shm_key = ftok(".", 8080);
        //child 负责连接
        int shmid = shmget(shm_key, 0, 0);//获取共享内存
        if(shmid < 0)
        {
            perror("shmget");
            return 1;
        }
        int *pticket_child = (int *)shmat(shmid, nullptr, 0);//获取ticket

        int semid = semget(key, 1, 0);//获取信号量
        //cout << "child semid " << semid <<endl;
        if(semid < 0)
        {
            perror("semget");
            return 2;
        }
        while (1)
        {
            // child
            struct sembuf sb; // P
            sb.sem_flg = 0;   //阻塞拿
            sb.sem_num = 0;   //拿第一个
            sb.sem_op = -1;   //拿一个

            if (semop(semid, &sb, 1) == -1 && EAGAIN == errno)
                break;
            //cout << "child " << *pticket_child << endl;
            //获得了这个信号量
            if (*pticket_child <= 0)
            {
                semctl(semid, 0, IPC_RMID);
                break;
            }
            --*pticket_child;
            cout << "child get ticket: " << *pticket_child << endl;

            usleep(1);

            // V
            struct sembuf sb2;
            sb2.sem_flg = 0;
            sb2.sem_num = 0;
            sb2.sem_op = 1;
            semop(semid, &sb2, 1);
        }
        shmdt(pticket_child);
    }
    else
    {
        int key = ftok("sem_sys.cc", 9000);
        int shm_key = ftok(".", 8080);
        int shmid = shmget(shm_key, 4096, IPC_CREAT | IPC_EXCL | 0644);
        if(shmid < 0)
        {
            perror("shmget");
            return 1;
        }
        int *pticket = (int *)shmat(shmid, nullptr, 0);

        *pticket = 1000; // 1000张票

        int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0644);
        //cout << "father semid " << semid <<endl;
        if(semid < 0)
        {
            perror("semget");
            return 2;
        }
        semun se;
        se.val = 1;
        // SETVAL   IPC_SET
        int res = semctl(semid, 0, SETVAL, se);
        if(res == -1)
        {
          perror("semctl");
          return 4;
        }
        int *pticket_father = pticket;
        while (1)
        {
            // father
            struct sembuf sb; // P
            sb.sem_flg = 0;
            sb.sem_num = 0;
            sb.sem_op = -1;

            if (semop(semid, &sb, 1) == -1  && EAGAIN == errno)
                break;

            //获得了这个信号量
            //cout << "father " << *pticket_father << " :" << &pticket_father << endl;
            if (*pticket_father <=   0)
            {
                semctl(semid, 0, IPC_RMID);
                break;
            }
            --*pticket_father;
            cout << "father get ticket: " << *pticket_father << " :" << &pticket_father << endl;

            usleep(1);

            // V
            struct sembuf sb2;
            sb2.sem_flg = 0;
            sb2.sem_num = 0;
            sb2.sem_op = 1;
            semop(semid, &sb2, 1);
        }
        shmdt(pticket_father);
        shmctl(shmid,IPC_RMID,nullptr);
    }
    return 0;
}

管道

【Linux】初识进程间通信
之前写的这篇文章由叙述,管道在65536内是支持原子性的,小于PIPE_BUF都能保证原子性,读写只能一端运行,内部就实现了互斥同步,要双向通信就开两个即可。

参考:
被遗忘的桃源——flock 文件锁
进程间锁:进程间pthread_mutex,文件锁

总结


  • 实际上我个人偏向于mmap的这种方式,因为当进程crash的时候,mmap可以保存到文件,但他和shm面临着共享内存被锁上无法使用的问题。
  • mmap和共享内存二者本质上是类似的,mmap可以看到文件的实体,而 shmget 对应的文件在交换分区上的 shm 文件系统内,无法直接cat 查看。
  • 因为文件锁flock在进程crash时,操作系统会清理掉,所以比较推荐使用flock作为进程的锁资源。(可以参考当中读读那个文章。)
  • 而信号量本身不能传递复杂消息,只能用来同步互斥。

进程间加锁目前我所知道的以上三种方式已经成列,由于能力有限,若有错误,欢迎评论区指出。

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

【Linux】如何在进程间加锁(实现互斥) 的相关文章

  • 抑制 makefile 中命令调用的回显?

    我为一个作业编写了一个程序 该程序应该将其输出打印到标准输出 分配规范需要创建一个 Makefile 当调用它时make run gt outputFile应该运行该程序并将输出写入一个文件 该文件的 SHA1 指纹与规范中给出的指纹相同
  • GLIBCXX_3.4.26 未找到在 BeagleBone 上运行交叉编译的程序

    我有以下程序 include
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c
  • 如何查明CONFIG_FANOTIFY_ACCESS_PERMISSIONS是否启用?

    我想利用fanotify 7 http man7 org linux man pages man7 fanotify 7 html我遇到的问题是在某些内核上CONFIG FANOTIFY ACCESS PERMISSIONS不起作用 虽然C
  • 大多数 Linux 系统头文件与 C++ 兼容吗?

    大多数 Linux 系统头文件 API C 兼容吗 今天我试图做这样的事情 include
  • 无法从 jenkins 作为后台进程运行 nohup 命令

    更新 根据下面的讨论 我编辑了我的答案以获得更准确的描述 我正在尝试从詹金斯运行 nohup 命令 完整的命令是 nohup java jar home jar server process 0 35 jar prod gt gt var
  • chown:不允许操作

    我有问题 我需要通过 php 脚本为系统中的不同用户设置文件所有者权限 所以我通过以下命令执行此操作 其中 1002 是系统的用户 ID file put contents filename content system chown 100
  • 所有平台上的java

    如果您想用 java 为 Windows Mac 和 Linux 编写桌面应用程序 那么所有这些代码都相同吗 您只需更改 GUI 即可使 Windows 应用程序更像 Windows 等等 如果不深入细节 它是如何工作的 Java 的卖点之
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • PHP 从命令行启动 gui 程序,但 apache 不启动

    首先 我阅读了有类似问题的人的一些帖子 但所有答案都没有超出导出 DISPLAY 0 0 和 xauth cookies 这是我的问题 提前感谢您的宝贵时间 我开发了一个小库 它使用 OpenGL 和 GLSL 渲染货架 过去几天我将它包装
  • Linux 可执行文件与 OS X“兼容”吗?

    如果您在基于 Linux 的平台上用 C 语言编译一个程序 然后将其移植以使用 MacOS 库 它会工作吗 来自编译器的核心机器代码在 Mac 和 Linux 上兼容吗 我问这个问题的原因是因为两者都是 基于 UNIX 的 所以我认为这是真
  • Linux:如何从特定端口发送TCP数据包?

    如何打开原始套接字以从特定 TCP 端口发送 我希望所有连接始终来自临时端口以下的一系列端口 如果您正在使用raw套接字 然后只需在数据包标头中填写正确的 TCP 源端口即可 相反 如果您使用 TCP 套接字接口 socket connec
  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • ubuntu:升级软件(cmake)-版本消歧(本地编译)[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我的机器上安装了 cmake 2 8 0 来自 ubuntu 软件包 二进制文件放置在 usr bin cmake 中 我需要将 cmake 版本至少
  • 在我的 index.php 中加载 CSS 和 JS 等资源时出现错误 403

    我使用的是 Linux Elementary OS 并在 opt 中安装了 lampp My CSS and JS won t load When I inspect my page through browser The console
  • jq中如何分组?

    这是 json 文档 name bucket1 clusterName cluster1 name bucket2 clusterName cluster1 name bucket3 clusterName cluster2 name bu
  • 查找哪个程序运行另一个程序

    我有一个 NAS 运行在 Redhat Linux 的有限版本上 我按照指示破解了它 这样我就可以访问 shell 这很有帮助 我还做了一些修改 其他人也做过修改 除了一个问题之外 它们似乎都工作得很好 不知何故 每隔 22 天 系统就会关

随机推荐

  • Java 多线程 --- 线程状态 Thread States

    Java 多线程 线程状态 Thread States New Threads Runnable Threads Blocked and Waiting Threads Terminated Threads New Threads 当用ne
  • 持续集成是什么?

    作者 阮一峰 日期 2015年9月23日 互联网软件的开发和发布 已经形成了一套标准流程 最重要的组成部分就是持续集成 Continuous integration 简称CI 本文简要介绍持续集成的概念和做法 一 概念 持续集成指的是 频繁
  • MyBatis流式查询

    MyBatis流式查询 1 应用场景说明 MyBatis preview JDBC三种读取方式 1 一次全部 默认 一次获取全部 2 流式 多次获取 一次一行 3 游标 多次获取 一次多行 在开发中我们经常需要会遇到统计数据 将数据导出到e
  • tomcat报错 Unable to process Jar entry

    错误描述 tomcat启动报下边的这个错 错误现象 09 Jul 2019 13 34 14 635 严重 RMI TCP Connection 3 127 0 0 1 org apache catalina startup Context
  • ORA-14402: 更新分区关键字列将导致分区的更改

    默认情况下 oracle的分区表对于分区字段是不允许进行update操作的 如果有对分区字段行进update 就会报错 ORA 14402 更新分区关键字列将导致分区的更改 但是可以通过打开表的row movement属性来允许对分区字段的
  • [JAVAee]Spring项目的创建与基本使用

    目录 Spring项目的创建 Spring中Bean对象的存储与获取 存储Bean对象 获取并使用Bean对象 getBean方法的重载 本文章介绍了Spring项目创建与使用的过程与一定的注意事项 Spring项目的创建 首先在IDEA中
  • 以太坊 Truffle实战

    目录 搭建私连网络 truflle初始化项目 智能合约示例 通用存证合约 初始化参数 接口 简易审批合约 初始化参数 数据结构 接口 智能合约的建立 谁在与智能合约交互 智能合约的销毁 整个过程主要演示chrome扩展 METAMASK O
  • 电源层和地线层完整性规则_电路设计规则

    电路图设计规则 一 电路设计流程 1 根据需求文件开始进行原理图设计 2 选择设计所需元器件 加载所需元件库 如果现有元件库没有所需元件 需自己制作 新制作的元器件要求功能形象化 标示准确化 选择合适的电路用于自己的设计完成相应的功能 确定
  • Unable to install breakpoint in XXX due to missing line number attributes.

    今天调试程序的时候 eclipse 弹出来一个 Unable to install breakpointdue to missingline number attributes Modify compileroptions togenera
  • 01.神经网络和深度学习——week2 神经网络基础(编程作业)

    Part 1 Python Basics with Numpy optional 1 Building basic functions with numpy 1 1 Sigmoid function np exp exe Build a f
  • Unity游戏开发客户端面经——C#(初级)

    前言 记录了总6w字的面经知识点 文章中的知识点若想深入了解 可以点击链接学习 由于文本太多 按类型分开 这一篇是C 常问问题总结 有帮助的可以收藏 1 引用类型 值类型 1 1 介绍 值类型 int bool float char str
  • 持续集成管理软件Jenkins应用实验

    编写Dockerfile文件 1 创建一个目录jenkins 保存相关的配置信息和内容 在 后输入mkdir Jenkins cd Jenkins命令 然后按Enter键 创建Jenkins目录并进入该目录 示例代码如下 root xian
  • UML建模与软件开发设计(七)——时序图设计

    在前面我们学习了类图相关知识 类图是一种静态结构模型视图 它是设计类及类间关系 即数据结构 的重要依据 但它无法刻画类的对象间的交互 通信行为 也就是说 类图无法描述类和类之间是如何通信 交互的 通俗地说 类图无法描述某个类的方法被哪个类所
  • OnTriggerEnter

    准备一个脚本 shiyan cs 脚本内容如下 脚本挂在小球上 然后运行场景 拖动小球撞盒子 然后再拖动盒子撞小球 分别看控制台打印结果 然后将脚本挂在盒子上 然后运行场景 拖动小球撞盒子 然后再拖动盒子撞小球 分别看控制台打印结果 OnT
  • 写一个Java程序,在程序中建立一个窗口

    编写一个Java程序 在程序中建立一个窗口 有四个文本框 两个按钮 单击 求和 按钮 能把第一个和第二个文本框的整数相加后结果显示在第三个文本框中 点击 复制 按钮 能将第三个文本框的内容复制到第四个文本框中 最后还可以正常关闭窗口 要创建
  • 后台管理系统----品牌管理

    目录 路由的搭建 品牌管理静态组件 品牌管理列表展示 element ui table表单组件 element ui el pagination分页器组件 插槽 网络请求 增删改牌静态页面 增删改品牌功能 书写api 增删改逻辑 表单校验
  • supervisor系列:4、子进程

    supervisor系列 4 子进程 文章目录 supervisor系列 4 子进程 1 非后台运行的子进程 1 1 程序配置示例 1 1 1 Apache 2 2 6 1 1 2 Two Zope 2 X instances and on
  • Windows命令行创建文件,文件夹,删除文件,文件夹命令

    创建文件夹命令 md 文件夹名字或者mkdir 文件夹名字 删除文件夹命令 rd删除空文件夹 rd s q 删除有子文件夹和子文件的文件夹 创建文件命令 type nul gt 可以带文件名 也可以不带文件名 这里用于空文件 echo 文件
  • mysql 常用函数

    一 mysql的函数 1 1 limit分页函数的使用 第一个起始的个数从0开始 第二个查询的个数 SELECT FROM student LIMIT 4 5 SELECT FROM student LIMIT 5 1 2 聚合函数 AVG
  • 【Linux】如何在进程间加锁(实现互斥)

    文章目录 前言 mmap 配合 pthread mutex t 先让多个进程能够看到一个num 多个进程互斥访问 具体代码 采用共享内存配合信号量 semget semctl semop 核心逻辑 管道 总结 前言 Linux 初识进程间通