Wake Me Up When September Ends.

A wanderer capable of grasping the beauty of the grass, with a heart full of ancient charm, and a fondness for playful wit. Those who understand my words are knowledgeable individuals; those who decipher my code truly comprehend the essence. I am a wandering code swordsman, carrying my skills and riding freely through the digital world.

之前写了很长时间的PHP,现在对PHP一些扩展以及swoole感兴趣,但是自己的c语言的基础太差几乎忘记一干二净。首先学一下c语言,c语言核心之一就是指针,所以这里应该记录一下这边学到函数指针,整理一下自己理解。

粗暴的理解,函数指针也是指针,只是存放了函数访问地址,函数名称可以理解为指针,涉及&与*互逆操作。程序员通过函数指针里面地址去访问函数,也就是调用函数!意味着如下代码是互等的:


(*pointer_foo_func)(10);
// 等同于
pointer_foo_func(10);

c语言如何定义函数指针

其实按照C语言规定,函数名本身就是指向函数代码的指针,通过函数名就能获取函数地址,同时也支持通过&获取函数地址,这一点比较特殊。也就是说调用函数可以如下:


void foo_func(int a);

void (*pointer_foo_func)(int) = &foo_func;
// 或
void (*foo_func_ptr)(int) = foo_func;

if (foo_func_ptr == foo_func) // true

使用

/**
 * 函数指针理解
 */

#include <stdio.h>

// 定一个函数
void foo_func(int a, void foo_func_call(int b));

// 定义回调函数
void foo_func_call(int b);

int main() {
    int a = 1;
    //函数名本质是函数指针常量
    foo_funcf("函数名调用:\n");
    foo_func(a, foo_func_call);
    foo_funcf("函数指针调用:\n");
    //定义一个函数指针
    //指针其实是一个变量,所以要预先申明先声明后才能使用的。所以函数指针变量要先声明。
    void (*foo_func_p)(int a, void (*foo_func_call)(int b)) = &foo_func; // 等同void (*foo_func_p)(int a, foo_func_call(int b)) = foo_func;
    (*foo_func_p)(a, foo_func_call);
    return 0;
}

/**
 * foo_func
 *
 * @param a
 * @param foo_func_call
 */
void foo_func(int a, void (*foo_func_call)(int b)) {
    foo_funcf("input:%d \n", a);
    foo_funcf("调用回调函数: \n");
    int b = 12;
    (*foo_func_call)(b);
}

/**
 * 回调函数
 *
 * @param  b
 */
void foo_func_call(int b) {
    foo_funcf("回调结果:%d \n", b);
}

可以通过typedef定一个函数类型简化调用代码


/**
 * 函数指针理解
 */

#include <stdio.h>

// 定义一个函数类型 定义函数类型不需要别名
typedef void (*func_type)(int b);

void foo_func(int a, func_type);

void foo_func_call(int b);


int main() {
    int a = 1;
    //函数指针
    void (*foo_func_p)(int a, func_type) = foo_func;
    printf("函数名调用:\n");
    foo_func(a, foo_func_call);
    printf("函数指针调用:\n");
    (*foo_func_p)(a, foo_func_call);
    return 0;
}

/**
 * foo_func
 *
 * @param a
 * @param foo_func_call
 */
void foo_func(int a, void (*foo_func_call)(int b)) {
    printf("input:%d \n", a);
    printf("调用回调函数: \n");
    int b = 12;
    (*foo_func_call)(b);
}

/**
 * 回调函数
 *
 * @param  b
 */
void foo_func_call(int b) {
    printf("回调结果:%d \n", b);
}




目前开发的笔记本使用的是ubuntu22.04.1 桌面版本,docker-desktop 正好有桌面版本,所以安装桌面版本比较合适。这边小记一下如何清除已有老旧docker,注意这里是完全清除老旧docker,原来的image和容器都会被清理!

步骤如下:

删除某软件及其安装时自动安装的所有包

sudo apt-get autoremove docker docker-ce docker-engine docker.io containerd runc

删除无用的相关的配置文件

dpkg -l | grep docker
dpkg -l |grep ^rc|awk '{print $2}' |sudo xargs dpkg -P 

卸载没有删除的docker相关插件,比如

sudo apt-get autoremove docker-ce-*

删除docker的相关配置,命令如:

sudo rm -rf /etc/systemd/system/docker.service.d
sudo rm -rf /var/lib/docker

最后再查询下docker相关软件包

dpkg -l | grep docker

如果还有包存在,则删除,可能会有多个

sudo apt remove --purge xxx# 
# 验证下 此处应该报错
docker -v

由于更换新电脑,需要生成新的密钥去免密登陆服务器,小记一下流程,免得下次还得搜索一下相关教程。

密钥登录的过程

SSH 密钥登录分为以下的步骤。

  1. 客户端通过ssh-keygen生成自己的公钥和私钥。
  2. 手动将客户端的公钥放入远程服务器的指定位置。
  3. 客户端向服务器发起 SSH 登录的请求。
  4. 服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。
  5. 客户端收到服务器发来的数据,使用私钥对数据进行签名,然后再发还给服务器。
  6. 服务器收到客户端发来的加密签名后,使用对应的公钥解密。若解密后数据一致,则允许用户登录。

生成公/私密钥

# 我这边是linux windows 系统可以在当前用户文件目录下自行创建该文件夹
cd ~/.ssh 
# 生成密钥 可使用-t参数,指定密钥的加密算法。默认RSA 其他算法可以搜索了解一下 然后一路回车即可
# 中间有询问是否要为私钥文件设定密码保护(passphrase)相当于二次密码保护默认可以不需要
ssh-keygen

上传公钥到服务器上

生成密钥以后,公钥必须上传到服务器,才能使用公钥登录。公钥是以.pub 结尾不要和私钥搞混了。

一般我习惯手动上传公钥到服务器~/.ssh/authorized_keys文件。这里需要注意你要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的~/.ssh/authorized_keys 中。

手动上传就是直接将公钥里面文本直接追加到~/.ssh/authorized_keys里面。

另外还可以使用ssh-copy-id 命令进行自动上传,本质就是自动把公钥里面文本直接追加到~/.ssh/authorized_keys里面。密令如下:

ssh-copy-id -i pub_key_file user@host

上面命令中,-i参数用来指定公钥文件,user是所要登录的账户名,host是服务器地址。如果省略用户名,默认为当前的本机用户名。执行完该命令,公钥就会拷贝到服务器。

配置config

配置config目的在于简化ssh登录命令,在当前用户 ~/.ssh/ 新建config文本 若存在无需创建。

配置如下:

Host zyimm
    HostName 192.168.1.1
    User tom
    Port 22
    IdentityFile file_key

配置文本格式,只需要同级别空格对齐即可。

HostName

需要ssh连接过去的主机名,一般是IP地址。

User

登录主机的用户名

IdentityFile

认证证书文件,默认位置是~/.ssh/id_rsa, ~/ssh/id_dsa等,如果采用默认的证书,可以不用设置此参数,除非你的证书放在某个自定义的目录,那么你就需要设置该参数来指向你的证书

Port

SSH访问主机的端口号,默认是22端口,同上,只有在非默认情况下才需要设置该值

使用

ssh  zyimm # 即可免密登陆192.168.1.1服务器了

如果使用vscode 建议下载 Remote-ssh 扩展,搭配使用更舒服!

记得配置之后需要重启服务器上的ssh服务!常见重启ssh服务命令如下(选择其中之一即可):

service sshd restart
systemctl restart sshd.service
/etc/init.d/ssh restart

调试

如果在上面步骤操作完之后,仍然出现一些问题,可以在命令中带上-v以便定位问题

ssh  zyimm  -v # 

snap-store自身更新

目前开发的笔记本使用的是ubuntu22.04.1 版本,默认内置snap商店无法完成自身更新,所以小记一下解决办法。

解决办法如下:

# root  账号无需加上sudo
sudo killall snap-store

sudo snap refresh snap-store

禁止自动更新

目前snap 稳定版本通道已经支持禁止自动更新,如果下述命令不起作用或报错,请先同上述进行snap-store自身更新

全部自动禁止更新

snap refresh --hold

禁用某个软件更新,比如火狐firefox

snap refresh --hold firefox

通过 –unhold 参数重新启用自动更新

snap refresh --unhold
# 同理恢复某个软件更新,如火狐
snap refresh --unhold firefox