linux下文件锁使用总结

news2024/11/25 13:44:42

     linux锁存在强制锁(mandatory lock)和劝告锁(advisory lock)。所谓强制锁,就是一个进程获取了那把锁(只有一把钥匙),只有一个进程可以操作,别的进程无能为力。所谓劝告锁,本质是一种协议,你访问文件前,先检查锁,这时候锁才其作用,如果不检查就要读写,那么劝告锁没有任何的作用。为了遵守协议,读写前先检查锁的那些进程通常称为合作进程。linux下可以使用fcntl,flock,lockf三个函数对文件上锁。

目录

1.fcntl(),flock(),lockf()三者区别和联系

1.1 flock和lockf/fcntl上锁互不影响

1.2 fcntl强制锁测试

2.fcntl()文件加锁(对文件部分或者整体)

2.1nginx使用fcnt实现进程同步(accept锁的一种实现)

3.flock文件加锁(对整个文件加锁)

2.1 flock说明1

2.2 锁继承与释放的语义

2.3 flock()的限制

4.lockf()文件加锁


1.fcntl(),flock(),lockf()三者区别和联系

(1)fcntl(),flock()是系统调用,lockf()是Glibc中的库函数,lockf()是对fcntl()的封装
(2)flock和lockf/fcntl加锁锁互不影响
(3) flock函数只能对整个文件上锁,这是于fcntl/lockf可以对文件的某一部分上锁
(4)flock可以有共享锁和排它锁,lockf只支持排它锁,fcntl里面参数flock可以有RDLCK读锁
(5)flock和fcntl/lockf在fork和dup时候也不相同
(6)flock不能在NFS文件系统上使用,在NFS中使用fcntl
 (7) 三个锁都可以递归,进程终止时,三者建立的所有文件锁都会被释放
(8)关闭一个描述符时,调用fcntl/lockf的进程通过该描述符引用文件上的任何一把锁都被释放(这些锁都是该进程设置的),flock则不是。
    例如:
        fd1 = open(pathname, …);
        lockf(fd1, F_LOCK, 0);
        fd2 = dup(fd1);
        close(fd2);
        则在close(fd2)后,在fd1上设置的锁会被释放,如果将dup换为open,以打开另一描述符上的同一文件,则效果也一样。
        fd1 = open(pathname, …);
        lockf(fd1, F_LOCK, 0);
        fd2 = open(pathname, …);
        close(fd2);

(9)fcntl/lockf获取的锁当fork产生的子进程不继承父进程所设置的锁,flock则不是。
(原因:flock创建的锁是和文件打开表项(struct file)相关联的,而不是fd,所以复制出了fd都可以操作这把锁,所以子进程继承了父进程的锁。flock里面要关闭所有复制出的fd,锁才会释放)
(10)在执行exec后,新程序可以继承原程序的锁,三者相同。(说明:如果对fd设置了close-on-exec,则exec前会关闭fd,相应文件的锁也会被释放)
(11)fcntl/lockf支持强制锁,flock则不是。
     fcntl支持强制性锁:对一个特定文件打开并设置组ID位(S_ISGID),并关闭其组执行位(S_IXGRP),则对该文件开启了强制性锁机制。
     在Linux中如果要使用强制性锁,则要在文件系统mount时,使用-omand打开该机制。    

1.1 flock和lockf/fcntl上锁互不影响

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>

int main(int argc, char **argv)
{
    int fd, ret;
    int pid;
    printf("pid: %d\n", getpid());
    fd = open("./test-flock.txt", O_RDWR| O_CREAT);
    ret = flock(fd, LOCK_EX);
    perror("flock");
    printf("flock return ret : %d\n", ret);
    ret = lockf(fd, F_LOCK, 0);
    perror("flock");
    printf("lockf return ret: %d\n", ret);
    sleep(999999);
    return 0;
}

编译运行:

查看该进程锁状态:

/proc/locks下面有锁的信息含义如下:

1) POSIX 和 FLOCK 表示锁类型。flock系统调用产生的是FLOCK,fcntl调用F_SETLK,F_SETLKW或者lockf产生的是POSIX类型
2) ADVISORY表明是劝告锁
3) WRITE表示是写锁,另外还有读锁
4) 7473是持有锁的进程ID
5) 103:02:7100699表示对应磁盘文件所在设备的主设备号和次设备号,还有文件的inode number。
6) 0表示锁的起始位置
7) EOF表示的是结束位置
6)和7)两个字段对fcntl/lockf有用,对flock来是总是0和EOF

1.2 fcntl强制锁测试

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <fcntl.h>
 
int main(int argc, char **argv) {
  if (argc > 1) {
    int fd = open(argv[1], O_WRONLY);
    if(fd == -1) {
      perror("open");
      exit(1);
    }
    static struct flock lock;
 
    lock.l_type = F_WRLCK;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len = 0;
    lock.l_pid = getpid();
 
    int ret = fcntl(fd, F_SETLKW, &lock);
    printf("Return value of fcntl:%d\n",ret);
    if(ret==0) {
        sleep(99999);
    }
  }
}

1) 使用mount命令带“mand”参数重新挂载根文件系统

 2)建立两个测试文件

touch mandatory.txt   

touch advisory.txt

3)查看文件属性

4) mandatory.txt 设置组ID位(S_ISGID),关闭执行位(S_IXGRP)

chmod g+s,g-x mandatory.txt

5)查看文件属性

 6)测试劝告锁

文件设置锁:

绕过锁修改文件:

 结论:绕过锁可以轻松的往文件中写入数据。 

7)测试强制锁

文件上锁:

 绕过锁修改文件:

 结论:绕过锁不能往文件中写入数据。 

2.fcntl()文件加锁(对文件部分或者整体)

     当有多个进程(线程)要访问同一个文件的时候,为了防止问导致的不一致,就需要考虑同步问题。fcntl是linux系统提供的系统api,可以使用它来给文件指定部分进行加锁。其声明如下:
int fcntl(int filedes, int cmd, ... /* struct flock *flockptr */ ); 
参数说明:
    该函数拥有可变参数的函数声明,该函数除了可以给文件上锁,还可以指定fd属性,例如:网络编程指定io fd的阻塞和非阻塞属性时常用
    filedes:是要操作的文件描述符,对与记录锁相关的操作
    cmd:相关锁操作,只能是F_GETLK, F_SETLK, 或者 F_SETLKW
        F_GETLK:从内核获取文件锁的信息,将其保存到第三个参数
        F_SETLK:设置非阻塞文件锁,第三个参数传入锁设置
        F_SETLKW:设置阻塞文件锁,第三个参数传入锁设置
    flockptr:flock结构体的指针,文件加解锁锁时才使用,其定义如下:
        struct flock {
            short l_type;/*F_RDLCK, F_WRLCK, or F_UNLCK */
            off_t l_start;/*offset in bytes, relative to l_whence */
            short l_whence;/*SEEK_SET, SEEK_CUR, or SEEK_END */
            off_t l_len;/*length, in bytes; 0 means lock to EOF */
            pid_t l_pid;/*returned with F_GETLK */
        };
        l_type:只读锁,读写锁,或是解锁
                F_RDLCK: 读锁,共享锁
                F_WRLCK: 写锁
                F_UNLCK: 解锁
        l_start:相对于l_whence的位置(细定位)
        l_whence:加锁的开始位置(粗定位)
            SEEK_SET:文件开头
            SEEK_CUR:文件当前位置
            SEEK_END:文件末尾位置
        l_len:是加锁的长度,
        l_pid:是加锁进程的进程id,由文件锁自动设置
        说明:
            加锁起始位置:l_whence + l_start
            加锁长度:l_len
        例如:对一个文件的前三个字节加读锁,则该结构体的l_type=F_RDLCK, l_start=0, l_whence=SEEK_SET, l_len=3
返回值:
    成功返回0,失败-1,errno被设置。
    
加读锁(共享锁)文件必须是读打开的,加写锁(排他锁)文件必须是写打开。
进程不能使用F_GETLK命令来测试它自己是否再文件的某一部分持有一把锁。

//文件加锁
int file_lock(int fd) {
  struct flock lock_info;
  memset(&lock_info, 0x00, sizeof(lock_info));
  lock_info.l_type = F_WRLCK;
  lock_info.l_whence = SEEK_SET;
  lock_info.l_start = 0;
  lock_info.l_len = 0;
  return fcntl(fd, F_SETLK, &lock_info);
}

//文件解锁
int file_ulock(int fd, bool lock) {
  struct flock lock_info;
  memset(&lock_info, 0x00, sizeof(lock_info));
  lock_info.l_type = F_UNLCK;
  lock_info.l_whence = SEEK_SET;
  lock_info.l_start = 0;
  lock_info.l_len = 0;
  return fcntl(fd, F_SETLK, &lock_info);
}

2.1nginx使用fcnt实现进程同步(accept锁的一种实现)

ngx_err_t
ngx_trylock_fd(ngx_fd_t fd)
{
    struct flock  fl;

    ngx_memzero(&fl, sizeof(struct flock));
    fl.l_type = F_WRLCK;
    fl.l_whence = SEEK_SET;

    if (fcntl(fd, F_SETLK, &fl) == -1) {
        return ngx_errno;
    }

    return 0;
}


ngx_err_t
ngx_lock_fd(ngx_fd_t fd)
{
    struct flock  fl;

    ngx_memzero(&fl, sizeof(struct flock));
    fl.l_type = F_WRLCK;
    fl.l_whence = SEEK_SET;

    if (fcntl(fd, F_SETLKW, &fl) == -1) {
        return ngx_errno;
    }

    return 0;
}


ngx_err_t
ngx_unlock_fd(ngx_fd_t fd)
{
    struct flock  fl;

    ngx_memzero(&fl, sizeof(struct flock));
    fl.l_type = F_UNLCK;
    fl.l_whence = SEEK_SET;

    if (fcntl(fd, F_SETLK, &fl) == -1) {
        return  ngx_errno;
    }

    return 0;
}

3.flock文件加锁(对整个文件加锁)

#include <sys/file.h>

int flock (int fd, int operation);
fd:文件对应描述符fd
operation:操作方式
    LOCK_SH:共享锁
    LOCK_EX:互斥锁
    LOCK_UN:解锁
    LOCK_NB:非阻塞
返回值:Returns 0 on success, or -1 on error

2.1 flock说明1

   在默认情况下,如果另一个进程已经持有了文件上的一个不兼容的锁,那么flock()会阻塞。
如果需要防止这种情况的出现,可以在operation参数中对这些值取OR(|)。
在这种情况下,如果一个进程已经持有了文件上的一个不兼容锁,那么flock()就会阻塞,相反,它会返回-1,并将errno设置成EWOULDBLOCK。
任意数量的进程可同时持有一个文件上的共享锁,但任意时刻只能有一个进程能够持有一个文件上的互斥锁,进程A先设置了锁,
进程B后设置锁的支持情况如下:
A进程                B进程
                 LOCK_SH       LOCK_EX  
LOCK_SH          是              否
LOCK_EX          否              是

无论程序以什么模式打开了文件(读、写或者读写),该文件上都可以放置一把共享锁或互斥锁。
在实际操作过程中,参数operation可以指定对应的值将共享锁转换成互斥锁(反之亦然)。
将一个共享锁转换成互斥锁,如果另一个进程要获取该文件的共享锁则会阻塞,除非operation参数指定了LOCK_NB标记,即:(LOCK_SH | LOCK_NB)。
锁的转换过程不是一个原子操作,在转换的过程中首先会删除既有的锁,然后创建新锁。
    

2.2 锁继承与释放的语义

        flock()根据operation参数传入LOCK_UN的值来释放一个文件锁。此外锁会在相应的文件描述符被关闭之后自动释放。同时当一个文件描述符被复制时(dup()、dup2()、或一个fcntl() F_DUPFD操作),新的文件描述符会引用同一个文件锁。

flock(fd, LOCK_EX);
new_fd = dup(fd);
flock(new_fd, LOCK_UN);
上面代码先在fd上设置一个互斥锁,然后通过fd创建一个指向相同文件的新文件描述符new_fd,最后通过new_fd来解锁。
新的文件描述符指向了同一个锁。所以如果通过一个特定的文件描述符获取了一个锁并且创建了该描述符的一个或多个副本后,如果不显示的调用一个解锁操作,只有当文件描述符副本都被关闭了之后锁才会被释放。

如果使用fork()创建一个子进程,子进程会复制父进程中的所有描述符,从而使得它们也会指向同一个文件锁。
如下代码会导致一个子进程删除一个父进程的锁:
flock (fd, LOCK_EX);
if (0 == fork ()) {
    flock (fd, LOCK_UN);
}
有时候可以利用这些语义来将一个文件锁从父进程传输到子进程:在fork()之后,父进程关闭其文件描述符,然后锁就只在子进程的控制之下了。
通过fork()创建的锁在exec()中会得以保留(除非在文件描述符上设置了close-on-exec标记并且该文件描述符是最后一个引用底层的打开文件描述的描述符)。
如果程序中使用open()来获取第二个引用同一个文件的描述符,那么flock()会将其视为不同的文件描述符。
如下代码会在第二个flock()上阻塞。
fd1 = open ("test.txt", O_RDWD);
fd2 = open ("test.txt", O_RDWD);
flock (fd1, LOCK_EX);
flock (fd2, LOCK_EX);


2.3 flock()的限制

flock()放置的锁有如下限制
(1)只能对整个文件进行加锁。这种粗粒度的加锁会限制协作进程间的并发。假如存在多个进程,其中各个进程都想同时访问同一个文件的不同部分。
(2)flock()只能放置劝告式锁(非强制的,一个进程给文件设置了锁,另一个进程不设置锁可以直接操作文件)。也就是一个进程可以简单地忽略另一个进程在文件上放置的锁。要使得劝告式加锁模型能够正常工作,所有访问文件的进程都必须要配合,即在执行文件IO之前先放置一把锁。
(3)很多NFS实现不识别flock()放置的锁。


4.lockf()文件加锁

函数原型:
#include <unistd.h>

int lockf(int fd, int cmd, off_t len);
参数说明:
   fd:通过open返回的打开文件描述符。
   cmd:
       F_LOCK:给文件互斥加锁,若文件以被加锁,则会一直阻塞到锁被释放。
       F_TLOCK:同F_LOCK,但若文件已被加锁,不会阻塞,而回返回错误。
       F_ULOCK:解锁。
       F_TEST:测试文件是否被上锁,若文件没被上锁则返回0,否则返回-1。
   len:从文件当前位置的起始要锁住的长度。
返回值:
    On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
通过函数参数的功能,可以看出lockf只支持排他锁,不支持共享锁。

flock和lockf/fcntl上锁互不影响

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>

int main(int argc, char **argv)
{
    int fd, ret;
    int pid;
    printf("pid: %d\n", getpid());
    fd = open("./test-flock.txt", O_RDWR| O_CREAT);
    ret = flock(fd, LOCK_EX);
    perror("flock");
    printf("flock return ret : %d\n", ret);
    ret = lockf(fd, F_LOCK, 0);
    perror("flock");
    printf("lockf return ret: %d\n", ret);
    sleep(999999);
    return 0;
}

编译运行:

查看该进程锁状态:

 

/proc/locks下面有锁的信息含义如下:

1) POSIX 和 FLOCK 表示锁类型。flock系统调用产生的是FLOCK,fcntl调用F_SETLK,F_SETLKW或者lockf产生的是POSIX类型
2) ADVISORY表明是劝告锁
3) WRITE表示是写锁,另外还有读锁
4) 7473是持有锁的进程ID
5) 103:02:7100699表示对应磁盘文件所在设备的主设备号和次设备号,还有文件的inode number。
6) 0表示锁的起始位置
7) EOF表示的是结束位置
6)和7)两个字段对fcntl/lockf有用,对flock来是总是0和EOF

fcntl强制锁测试

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <fcntl.h>
 
int main(int argc, char **argv) {
  if (argc > 1) {
    int fd = open(argv[1], O_WRONLY);
    if(fd == -1) {
      perror("open");
      exit(1);
    }
    static struct flock lock;
 
    lock.l_type = F_WRLCK;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len = 0;
    lock.l_pid = getpid();
 
    int ret = fcntl(fd, F_SETLKW, &lock);
    printf("Return value of fcntl:%d\n",ret);
    if(ret==0) {
        sleep(99999);
    }
  }
}

1.使用mount命令带“mand”参数重新挂载根文件系统

 2.建立两个测试文件

touch mandatory.txt   

touch advisory.txt

3. 查看文件属性

4.mandatory.txt 设置组ID位(S_ISGID),关闭执行位(S_IXGRP)

chmod g+s,g-x mandatory.txt

5.查看文件属性

 5.测试劝告锁

文件设置锁:

绕过锁修改文件:

 结论:绕过锁可以轻松的往文件中写入数据。 

6.测试强制锁

文件上锁:

 绕过锁修改文件:

 结论:绕过锁不能往文件中写入数据。 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/650129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据库】Mysql索引、事务与存储引擎

文章目录 一、索引介绍1. 索引的概念2. 索引的作用与副作用2.1 索引的作用2.2 索引的副作用2.3 如何实现索引 3. 创建索引的原则依据4. 索引的分类和创建4.1 普通索引直接创建索引修改表方式创建创建表的时指定索引 4.2 唯一索引直接创建唯一索引修改表方式创建创建表的时候指定…

实测Maven依赖包可通过域名抢注实现钓鱼攻击吗

先说结论&#xff1a;基本不可行 原理 Maven包中 groupId 字段是域名反写&#xff0c;比如你有一个 12345.com&#xff0c;就可以申请到 com.12345 的groupId。 很多开源项目都停止维护&#xff0c;但是很多人使用&#xff0c;这些团队可能忘记续费域名&#xff1b;同时目前…

keras运行debug解决protobuf版本冲突

# code 5.4 使用Keras实现异或网络 import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Activation from tensorflow.keras.optimizers import SGD x_train np.array([[0, 0],[0, 1],[1, 0],[1, 1] ]) y_train …

她98年的,我玩不过她...┭┮﹏┭┮

现在的小年轻真的卷得过分了。前段时间我们公司来了个98年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里条…

高级VLAN_vlan聚合(Super VLAN)实验

vlan聚合(Super_VLAN) 就是在一个物理网络里面用多个vlan来隔离广播域,将这个vlan聚合成一个逻辑vlan就有了Super vlan,这些多个vlan共用一个ip子网和网关, 目的就是为了节约ip地址资源 普通vlan示意图 普通vlan就存在很多网关,有的vlan可能会存在只有20个主机设备,但是需要划…

罗德FSH13手持式频谱分析仪RohdeSchwarz

FSH4和R&S?FSH8是罗德与施瓦茨公司全新推出面向未来应用的手持式频谱分析仪。它集频谱分 析、天馈线分析、全功能矢量网络分析、矢量电压表、功率计主机、宽带通信解调等多种测试功能 于一身&#xff0c;拥有媲美中高档台式频谱仪的指标。R&S?FSH4和R&S?FSH8完美…

023+limou+C语言的“可变参数列表”和“命令行参数”以及“递归调用”

0.前言 您好&#xff0c;这里是limou3434的一篇博文&#xff0c;感兴趣可以看看我的其他内容。本次我给您带来了C语言的“可变参数列表”&#xff0c;要明白这些内容&#xff0c;您可能需要重新复习下C语言视角的栈帧空间知识。最后我还给出两个小的C语言知识点&#xff1a;“…

Metasploit渗透测试框架

文章目录 Metasploit渗透测试框架基于TCP协议收集主机信息开放端口扫描db_nmap查询网段内在线存活的主机半连接的方式进行半连接扫描使用auxiliary/sniffer下的psunffle模块进行密码嗅探 基于SNMP协议收集主机信息基于SSH协议收集主机信息实战-制作Linux恶意病毒获取公司服务权…

JAVA开发(通过Apollo注入配置信息的几种方式)

前言 在springCloud中有一个重要的组件就是配置中心&#xff0c;config:server&#xff0c;用于配置springboot中需要注入的各种配置项。但是现在发现越来越多的企业使用Apollo进行集成。博主在开发中也是使用Apollo进行配置。本文总结Apollo的的使用&#xff0c;集成到spring…

大模型入门(四)—— 基于peft 微调 LLaMa模型

llama-7b模型大小大约27G&#xff0c;本文在单张/两张 16G V100上基于hugging face的peft库实现了llama-7b的微调。 1、模型和数据准备 使用的大模型&#xff1a;https://huggingface.co/decapoda-research/llama-7b-hf&#xff0c;已经是float16的模型。 微调数据集&#x…

ASEMI代理光宝光耦LTV-0314的应用与优势

编辑-Z 在电子设备的设计和制造过程中&#xff0c;光耦合器是一种至关重要的组件。它们在电路中起到隔离作用&#xff0c;保护电子设备免受电压冲击和电流过载的影响。今天&#xff0c;我们将深入探讨一种特殊的光耦合器——LTV-0314&#xff0c;它的特性、应用以及优势。 一、…

细说如何封装一个日历组件(多视图、可选择、国际化)

前言 最近好奇日历组件是怎么实现的。于是阅读了下react-calendar的源码&#xff0c;并实现了简化版的日历组件。本文把实现日历的设计思路分享给大家。只要理清了主要逻辑&#xff0c;就不难实现了。 技术栈&#xff1a;react、typescript 预览 在线预览demo&#xff1a;c…

亚马逊云科技中国峰会:探索强化学习的未来与Amazon DeepRacer赛车比赛

目录 一、如何构建自己的第一个强化学习模型第一步: 创建 AWS DeepRacer 资源第二步: 定义你的赛道第三步: 训练你的模型第四步: 优化你的模型第五步: 在仿真器中测试你的模型第六步: 在真实赛道上测试你的模型 二、Amazon DeepRacer 中国峰会总决赛三、Amazon DeepRacer 自动驾…

Redis基础+使用+八股文!万字详解一篇就够!

一、目标 学习Redis基础必须掌握的内容&#xff1a; 了解 Redis 以及缓存的作用&#xff1b;掌握 Redis 5 大基本数据类型的使用&#xff1b;掌握常见Redis 面试题&#xff1b;掌握 Redis 的持久化功能&#xff1b;了解 Redis 集群功能。 二、什么是缓存&#xff1f; 缓存定义…

Netty中PileLine类介绍

一、Netty基本介绍 Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具&#xff0c;用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty 在保证易于开发的同时还保证了其应用的性能&#xff0c;稳定性和伸缩性。 Netty 是一…

VTK Filter 总结

源对象 成像滤波器 可视化滤波器 可视化滤波器&#xff08;输入类型vtkDataSet&#xff09;。 可视化滤波器&#xff08;输入类型vtkPointSet) 可视化滤波器&#xff08;输入类型vtkPolyData) 可视化滤波器&#xff08;(输入类型vtkStructuredGrid)。 可视化滤波器&#xff08;…

浅析视频监控技术及AI发展趋势下的智能化视频技术应用

视频监控技术是指通过摄像机对指定区域进行实时视频直播、录制、传输、存储、管理和分析的技术系统。它可以用于监控各种场所&#xff0c;如校园、工厂、工地、工作场所、公共区域、交通工具等。视频监控技术主要涉及到以下几个部分&#xff1a; 1、摄像机 摄像机是视频监控技…

三年软件测试外包的我也没能转正

外包的群体庞大&#xff0c;很多企业为了节约高昂的人力成本&#xff0c;会把一些非核心业务承包给外包公司&#xff0c;这些工作往往是阶段性、辅助性&#xff0c;没有什么技术含量&#xff0c;而且由于外包人员不是与大厂签订劳动合同&#xff0c;因此&#xff0c;他们更像是…

图像点运算之灰度变换之非线性变换

目录 note code test note 图像点运算之灰度变换之非线性变换 例如&#xff1a;y 10 * x ^ 0.5 code void noline_convert_fun(uchar& in, uchar& out) {out 10 * (uchar)pow((float)in, 0.5); } void img_nonline_convert(Mat& src, Mat& res) {if (s…

html好看的登录界面2(十四种风格登录源码)

文章目录 1.登录风格效果说明1.1 凹显风登录界面1.2 大气简洁风登录界面1.3 弹出背景风登录界面1.4 动态左右切换风登陆界面1.5 简洁背景切换登录界面1.6 可关闭登录界面1.7 蒙蒙山雨风登录界面1.8 苹果弹框风登录界面1.9 上中下青春风登录界面1.10 夏日风登录界面1.11 星光熠熠…