❶ 线程特有数据(Thread Specific Data)
在单线程程序中,我们经常要使用 全局变量 来实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时在应用程序设计中有必要提供 线程私有 的全局变量,仅在某个线程中有效,但可以跨多个函数访问,这样每个线程访问它自己独立的数据空间,而不用担心和其它线程的同步访问。
这样在一个线程内部的各个函数都能访问、但其它线程不能访问的变量,我们就需要使用 线程局部静态变量 (Static memory local to a thread) 同时也可称之为 线程特有数据 (Thread-Specific Data 或 TSD),或者 线程局部存储 (Thread-Local Storage 或 TLS)。
POSIX 线程库提供了如下 API 来管理线程特有数据(TSD):
第一参数 key 指向 pthread_key_t 的对象的指针。请 注意 这里 pthread_key_t 的对象占用的空间是用户事先分配好的, pthread_key_create 不会动态生成 pthread_key_t 对象。
第二参数 desctructor ,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。
有时我们在线程里初始化时,需要避免重复初始化。我们希望一个线程里只调用 pthread_key_create 一次,这时就要使用 pthread_once 与它配合。
第一个参数 once_control 指向一个 pthread_once_t 对象,这个对象必须是常量 PTHREAD_ONCE_INIT ,否则 pthread_once 函数会出现不可预料的结果。
第二个参数 init_routine ,是调用的初始化函数,不能有参数,不能有返回值。
如果成功则返回0,失败返回非0值。
创建完键后,必须将其与线程数据关联起来。关联后也可以获得某一键对应的线程数据。关联键和数据使用的函数为:
第一参数 key 指向键。
第二参数 value 是欲关联的数据。
函数成功则返回0,失败返回非0值。
注意: 用 pthread_setspecific 为一个键指定新的线程数据时,并不会主动调用析构函数释放之前的内存,所以调用线程必须自己释放原有的线程数据以回收内存。
获取与某一个键关联的数据使用函数的函数为:
参数 key 指向键。
如果有与此键对应的数据,则函数返回该数据,否则返回NULL。
删除一个键使用的函数为:
参数 key 为要删除的键。
成功则返回0,失败返回非0值。
注意: 该函数将键设置为可用,以供下一次调用 pthread_key_create() 使用。它并不检查当前是否有线程正在使用该键对应的线程数据,所以它并不会触发函数 pthread_key_create 中定义的 destructor 函数,也就不会释放该键关联的线程数据所占用的内存资源,而且在将 key 设置为可用后,在线程退出时也不会再调用析构函数。所以在将 key 设置为可用之前,必须要确定:
在 linux 中每个进程有一个全局的数组 __pthread_keys ,数组中存放着 称为 key 的结构体,定义类似如下:
在 key 结构中 seq 为一个序列号,用来作为使用标志指示这个结构在数组中是否正在使用,初始化时被设为0,即表示 不在使用 。 destructor 用来存放一个析构函数指针。
pthread_create_key 会从数组中找到一个还未使用的 key 元素,将其序列号 seq 加1,并记录析构函数地址,并将 key 在数组 __pthread_keys 中的 下标 作为返回值返回。那么如何判断一个 key 正在使用呢?
如果 key 的序列号 seq 为偶数则表示未分配,分配时将 seq 加1变成奇数,即表示正在使用。这个操作过程采用原子 CAS 来完成,以保证线程安全。在 pthread_key_delete() 时也将序列号 seq 加1,表示可以再被使用,通过序列号机制来保证回收的 key 不会被复用(复用 key 可能会导致线程在退出时可能会调用错误的析构函数)。但是一直加1会导致序列号回绕,还是会复用 key ,所以调用 pthread_create_key 获取可用的 key 时会检查是否有回绕风险,如果有则创建失败。
除了进程范围内的 key 结构数组外,系统还在进程中维护关于每个线程的控制块 TCB(用于管理寄存器,线程栈等),里面有一个 pthread_key_data 类型的数组。这个数组中的元素数量和进程中的 key 数组数量相等。 pthread_key_data 的定义类似如下:
根据 pthread_key_create() 返回的可用的 key 在 __pthread_keys 数组中的下标, pthread_setspecific() 在 pthread_key_data 的数组 中定位相同下标的一个元素 pthread_key_data ,并设置其序号 seq 设置为对应的 key 的序列号,数据指针 data 指向设置线程特有数据(TSD)的值。
pthread_getspecific() 用于将 pthread_setspecific() 设置的 data 取出。
线程退出时, pthread_key_data 中的序号 seq 用于判断该 key 是否仍在使用中(即与在 __pthread_keys 中的同一个下标对应的 key 的序列号 seq 是否相同),若是则将 pthread_key_data 中 data(即 线程特有数据 TSD)作为参数调用析构函数。
由于系统在每个进程中 pthread_key_t 类型的数量是有限的,所有在进程中并不能获取无限个 pthread_key_t 类型。Linux 中可以通过 PTHREAD_KEY_MAX(定义于 limits.h 文件中)或者系统调用 sysconf(_SC_THREAD_KEYS_MAX) 来确定当前系统最多支持多少个 key 。 Linux 中默认是 1024 个 key,这对大多数程序来书已经够了。如果一个线程中有多个线程局部存储变量(TLS),通常可以将这些变量封装到一个数据结构中,然后使用封装后的数据结构和一个线程局部变量相关联,这样就能减少对键值的使用。
https://blog.csdn.net/hustraiet/article/details/9857919
https://blog.csdn.net/hustraiet/article/details/9857919
https://blog.csdn.net/caigen1988/article/details/7901248
http://www.bitools.com/?p=2443
https://spockwangs.github.io/blog/2017/12/01/thread-local-storage/
https://www.jianshu.com/p/71c2f80d7bd1
https://blog.csdn.net/cywosp/article/details/26469435
http://www.embeddedlinux.org.cn/emblinuxappdev/117.htm
❷ linux建立TCP服务器后,TCP客户端与服务器连接成功后,怎样获取客户端的MAC地址
我认为你是从 socket中取不到这部分的信息的.
你得再操作 arp的缓存部分回才行. ARP那部分如果在特殊答情况,很乱套.比如有攻击或是IP地址设置有冲突啥地.
---
详细的C语言怎么操作ARP缓存我不太清楚. 但你如果想知道.就得查一下这部分怎么做了.
高层的socket操作是得不到的.
只有直连网段的计算机有MAC地址信息.经过路由来的数据包.取不到.
❸ linux宕机后根据地址来查看哪个函数出错
|1)从System.map文件中直接得到地址:
$ grep sys_open /usr/src/linux/System.map
2)使用 nm 命令:
$ nm vmlinuz | grep sys_open
3)从 /proc/kallsyms 文件获得地址:
$ cat /proc/kallsyms | grep sys_open
4)使用 kallsyms_lookup_name() 函数:版
是在kernel/kallsyms.c文件中定义的权,要使用它必须启用CONFIG_KALLSYMS编译内核。
kallsyms_lookup_name()接受一个字符串格式内核函数名,返回那个内核函数的地址。
kallsyms_lookup_name("sys_open");
❹ C语言 Linux 获取调用者函数名称
可以做到的,参考一下backtrace_symbols的实现以及相关原理,这里面比较复杂,也比较底层,一两句话说不清楚
你要是想通过_FUNCTION_这种方式来搞的话,可以通过如下方式:
#define m_fun(args) fun(__FILE__,__LINE__, args)
进行相应的替换即可
❺ 怎么用C语言获取Linux系统的网卡IP地址
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<net/if.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<sys/ioctl.h>
//获取地址
//返回IP地址字符串
intgetlocalip(char*outip)
{
inti=0;
intsockfd;
structifconfifconf;
charbuf=(char)malloc(512);
structifreq*ifreq;
char*ip;
//初始化ifconf
ifconf.ifc_len=512;
ifconf.ifc_buf=buf;
if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
{
return-1;
}
ioctl(sockfd,SIOCGIFCONF,&ifconf);//获取所有接口信息
close(sockfd);
//接下来一个一个的获取IP地址
ifreq=(structifreq*)buf;
i=ifconf.ifc_len/sizeof(structifreq);
char*pos=outip;
intcount;
for(count=0;(count<5&&i>0);i--)
{
ip=inet_ntoa(((structsockaddr_in*)&(ifreq->ifr_addr))->sin_addr);
if(strncmp(ip,"127.0.0.1",3)==0)//排除127.x.x.x,继续下一个
{
ifreq++;
continue;
}else
{
printf("%s ",ip);
strcpy(pos,ip);
intlen=strlen(ip);
pos=' ';
pos+=len+1;
count++;
ifreq++;
}
}
free(buf);
return0;
}
//——————————-函数的调用方式————————————-
intmain(intargc,char**argv)
{
charip={'*'};
if(getlocalip(ip)==0)
{
printf("本机IP地址是:%s ",ip);
}
else
{
printf("无法获取本机IP地址");
}
return0;
}
❻ 获取Linux IP地址的六种方法总结
本文总结六种查看Linux IP地址的方法,方便以后的运维开发工作。
在介绍前先学习一下三个命令行筛选的主要的指令,也是频繁使用到的命令。
1、head。 head 命令可用于查看文件的开头部分的内容,有一个常用的参数 -n 用于显示行数,默认为 10。
运行head --help查看说明信息:
-q 隐藏文件名
-v 显示文件名
-c<数目> 显示的字节数。
-n<行数> 显示的行数。
2、grep。 grep 命令用于查找文件里符合条件的字符串。运行grep --help查看说明信息,参数太多主要有以下几种:
grep -r递归选择。
grep -v反选,显示不包含匹配文本的所有行。
grep -n显示符合样式的那一行之前。
grep -A显示符合范本样式的那一列之外,并显示该行之后的内容。
3、awk。 强大的文本分析工具,命令使用过于复杂(awk --help),只需要知道 awk '{print$2}'为打印第二行数据。
4、tail 。tail命令可用于查看文件的结束部分的内容,有一个常用的参数 -n 用于显示行数,默认为 10。tail --help查看主要的参数:
tail -n显示最后多少行
tail -c显示最后十个字符
tail -f 循环读取,跟踪显示最后十行
5、cut。 显示每行从开头算起的文字。
cut -b :以字节为单位进行分割。
cut -c :以字符为单位进行分割
cut -d :自定义分隔符,默认为制表符
cut -f :与-d一起使用,指定显示哪个区域
无线网卡地址:
echo wlan0=`ifconfig wlan0 | head -n2 | grep inet | awk '{print$2}'`
有线网卡地址:
echo eth0=`ifconfig eth0 | head -n2 | grep inet | awk '{print$2}'`
或者命令:
ifconfig | grep "inet " | cut -d: -f2 | awk '{print $1}' | grep -v "^127."
无线网卡地址:
ip address | grep wlan0 | awk '{print$2}'
有线网卡地址:
ip address | grep eth0 | awk '{print$2}'
或者
echo eth0=`ip address show eth0 | head -n4 | grep inet | awk '{print$2}'
echo wlan0=`ip address show wlan0 | head -n4 | grep inet | awk '{print$2}'
运行hostname -help命令查看说明信息:
Program options:
-a, --alias alias names
-A, --all-fqdns all long host names (FQDNs)
-b, --boot set default hostname if none available
-d, --domain DNS domain name
-f, --fqdn, --long long host name (FQDN)
-F, --file read host name or NIS domain name from given file
-i, --ip-address addresses for the host name
-I, --all-ip-addresses all addresses for the host
-s, --short short host name
-y, --yp, --nis NIS/YP domain name
hostname -i得到环回地址127.0.1.1, hostname -I得到具体的网卡信息192.168.31.82 。
php语言查看ip就是使用函数shell_exec来执行shell命令。
比如:
<?php
echo shell_exec("echo wlan0=`ifconfig wlan0 | head -n2 | grep inet | awk '{print$2}'`");
?>
然后执行php ip.php 。shell_exec()里面可以放置任何shell命令。这个方法的意义在于php可以通过网页对外提供服务。
#!/usr/bin/env python
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
Local_wlan0=get_ip_address("wlan0")
Local_lo=get_ip_address("lo")
#Local_eth0=get_ip_address("eth0")
print Local_wlan0
print Local_lo
#print Local_eth0
利用socket包,然后执行python ip.py 得到wlan0信息。
#!/usr/bin/env python
import os
def get_ip():
out = os.popen("echo wlan0=`ifconfig wlan0 | head -n2 | grep inet | awk '{print$2}'`").read()
print out
if __name__ == '__main__':
get_ip()
和php的shell_exec函数类似,os.popen()里面可以放置任何shell命令。注意有个函数os.system的结果只是命令执行结果的返回值,执行成功为0;os.popen()可以读出执行的内容,输出的结果比较特殊,带换行符\n 。
❼ Linux下怎样在进程中获取虚拟地址对应的物理地址
Linux文件目录中的/proc记录着当前进程的信息,称其为虚拟文件系统。在/proc下有一个链接目录名为self,这意味着哪一个进程打开了它,self中存储的信息就是所链接进程的。self中有一个名为page_map的文件,专门用来记录所链接进程的物理页号信息。这样通过/proc/pid/page_map文件,允许一个用户态的进程查看到每个虚拟页映射到的物理页
/proc/pid/page_map中的每一项都包含了一个64位的值,这个值内容如下所示。每一项的映射方式不同于真正的虚拟地址映射,其文件中遵循独立的对应关系,即虚拟地址相对于0x0经过的页面数是对应项在文件中的偏移量
* /proc/pid/pagemap. This file lets a userspace process find out which
physical frame each virtual page is mapped to. It contains one 64-bit
value for each virtual page, containing the following data (from
fs/proc/task_mmu.c, above pagemap_read):
* Bits 0-54 page frame number (PFN) if present//present为1时,bit0-54表示物理页号
* Bits 0-4 swap type if swapped
* Bits 5-54 swap offset if swapped
* Bit 55 pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
* Bit 56 page exclusively mapped (since 4.2)
* Bits 57-60 zero
* Bit 61 page is file-page or shared-anon (since 3.5)
* Bit 62 page swapped
* Bit 63 page present//如果为1,表示当前物理页在内存中;为0,表示当前物理页不在内存中
在计算物理地址时,只需要找到虚拟地址的对应项,再通过对应项中的bit63判断此物理页是否在内存中,若在内存中则对应项中的物理页号加上偏移地址,就能得到物理地址
通过程序获取物理地址并验证写时拷贝技术
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
//计算虚拟地址对应的地址,传入虚拟地址vaddr,通过paddr传出物理地址
void mem_addr(unsigned long vaddr, unsigned long *paddr)
{
int pageSize = getpagesize();//调用此函数获取系统设定的页面大小
unsigned long v_pageIndex = vaddr / pageSize;//计算此虚拟地址相对于0x0的经过的页面数
unsigned long v_offset = v_pageIndex * sizeof(uint64_t);//计算在/proc/pid/page_map文件中的偏移量
unsigned long page_offset = vaddr % pageSize;//计算虚拟地址在页面中的偏移量
uint64_t item = 0;//存储对应项的值
int fd = open("/proc/self/pagemap", O_RDONLY);。。以只读方式打开/proc/pid/page_map
if(fd == -1)//判断是否打开失败
{
printf("open /proc/self/pagemap error
");
return;
}
if(lseek(fd, v_offset, SEEK_SET) == -1)//将游标移动到相应位置,即对应项的起始地址且判断是否移动失败
{
printf("sleek error
");
return;
}
if(read(fd, &item, sizeof(uint64_t)) != sizeof(uint64_t))//读取对应项的值,并存入item中,且判断读取数据位数是否正确
{
printf("read item error
");
return;
}
if((((uint64_t)1 << 63) & item) == 0)//判断present是否为0
{
printf("page present is 0
");
return ;
}
uint64_t phy_pageIndex = (((uint64_t)1 << 55) - 1) & item;//计算物理页号,即取item的bit0-54
*paddr = (phy_pageIndex * pageSize) + page_offset;//再加上页内偏移量就得到了物理地址
}
const int a = 100;//全局常量
int main()
{
int b = 100;//局部变量
static c = 100;//局部静态变量
const int d = 100;//局部常量
char *str = "Hello World!";
unsigned long phy = 0;//物理地址
char *p = (char*)malloc(100);//动态内存
int pid = fork();//创建子进程
if(pid == 0)
{
//p[0] = '1';//子进程中修改动态内存
mem_addr((unsigned long)&a, &phy);
printf("pid = %d, virtual addr = %x , physical addr = %x
", getpid(), &a, phy);
}
else
{
mem_addr((unsigned long)&a, &phy);
printf("pid = %d, virtual addr = %x , physical addr = %x
", getpid(), &a, phy);
}
sleep(100);
free(p);
waitpid();
return 0;
}
测试结果如下:
全局常量:符合写时拷贝技术
子进程修改动态内存
*其实想要知道虚拟地址对应的物理地址,通过这样的方式也可以得到物理地址而不用操作MMU。。。*
以上就是Linux下怎样在进程中获取虚拟地址对应的物理地址的全文介绍,希望对您学习和使用linux系统开发有所帮助.
❽ 为什么显示不出析构函数
分类: 电脑/网络 >> 程序设计 >> 其他编程语言
问题描述:
程序正确.照抄书上例题.但是结果却有点不同.显示不出析构函数.例题中注释的那行.应该要在结果中输出..可是其它都对,就是不输出..不知道为什么?
#include<iostream>
#include<string>
using namespace std;
class Student
{public:
Student(int a1,string a2)
{cout<<"构造函数!"<<a1<<endl;num=a1;nam=a2;}
~Student() {cout<<"析构函数!"<<num<<endl;}/**********此为析构函数要输出**************/
void print();
private:
int num;string nam;
}s1(101,"刘德华"),s2(0,"张学友");
void Student::print()
{if(num==0) throw num;
else cout<<num<<" "<<nam<<endl;}
void A()
{s1.print();s2.print();}
int main()
{try {A();}
catch(int a1) {cout<<"num="<<a1<<",错误!"<<endl;}
return 0;}
解析:
如果你要使用C++标准库(iostream中的cout即C++标准库的东东),请不要使用低版本的编译器,例如在VC6中,这段程序就不能正确执行,即不能输出析构函数里的语句。
使用VC7(里的)就可以正确执行,使用LINUX和UNIX的GCC也没问题。
当然你也可修改析构函数里的cout<<"析构函数!"<<num<<endl;这句话为:
printf("析构函数!%d\n", num);
这样在VC6中就可以输出,因为printf不是C++标准库的,而是C标准库的。
❾ linux系统函数怎么获取ip地址
ifconfig命令获取网卡地址,再过滤一下ip就出来了
ifconfig | grep '[0-9]\{1,3\}\.[0-9]\{1,3\}\.'|cut -d ":" -f2| cut -d " " -f1 | sed -n "1p"
❿ 有谁知道linux系统环境下,怎样在后台才能获取到mac地址
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 与系统相关的一些常用工具方法.
*
* @author stephen
* @version 1.0.0
*/
public class SystemTool {
/**
* 获取当前操作系统名称.
* return 操作系统名称 例如:windows xp,linux 等.
*/
public static String getOSName() {
return System.getProperty("os.name").toLowerCase();
}
/**
* 获取unix网卡的mac地址.
* 非windows的系统默认调用本方法获取.如果有特殊系统请继续扩充新的取mac地址方法.
* @return mac地址
*/
public static String getUnixMACAddress() {
String mac = null;
BufferedReader bufferedReader = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("ifconfig eth0");// linux下的命令,一般取eth0作为本地主网卡 显示信息中包含有mac地址信息
bufferedReader = new BufferedReader(new InputStreamReader(process
.getInputStream()));
String line = null;
int index = -1;
while ((line = bufferedReader.readLine()) != null) {
index = line.toLowerCase().indexOf("hwaddr");// 寻找标示字符串[hwaddr]
if (index >= 0) {// 找到了
mac = line.substring(index +"hwaddr".length()+ 1).trim();// 取出mac地址并去除2边空格
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
bufferedReader = null;
process = null;
}
return mac;
}
/**
* 获取widnows网卡的mac地址.
* @return mac地址
*/
public static String getWindowsMACAddress() {
String mac = null;
BufferedReader bufferedReader = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("ipconfig /all");// windows下的命令,显示信息中包含有mac地址信息
bufferedReader = new BufferedReader(new InputStreamReader(process
.getInputStream()));
String line = null;
int index = -1;
while ((line = bufferedReader.readLine()) != null) {
index = line.toLowerCase().indexOf("physical address");// 寻找标示字符串[physical address]
if (index >= 0) {// 找到了
index = line.indexOf(":");// 寻找":"的位置
if (index>=0) {
mac = line.substring(index + 1).trim();// 取出mac地址并去除2边空格
}
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
bufferedReader = null;
process = null;
}
return mac;
}
/**
* 测试用的main方法.
*
* @param argc
* 运行参数.
*/
public static void main(String[] argc) {
String os = getOSName();
System.out.println(os);
if(os.startsWith("windows")){
//本地是windows
String mac = getWindowsMACAddress();
System.out.println(mac);
}else{
//本地是非windows系统 一般就是unix
String mac = getUnixMACAddress();
System.out.println(mac);
}
}
}
-------------------------------------------------------------------------
本程序可以正确获得本机IP地址和网卡"eth0"的MAC地址,已经在windowsXP和ubuntu-Linux上测试过
(注意:如果有多块网卡,可能出错)
下面给出代码:
import java.net.*;import java.util.*;
public class Test { public static void main(String[] args) { Test t = new Test(); System.out.println(t.getLocalIP()); System.out.println(t.getMacAddr()); }
public String getMacAddr() { String MacAddr = ""; String str = ""; try { NetworkInterface NIC = NetworkInterface.getByName("eth0"); byte[] buf = NIC.getHardwareAddress(); for (int i = 0; i < buf.length; i++) { str = str + byteHEX(buf[i]); } MacAddr = str.toUpperCase(); } catch (SocketException e) { e.printStackTrace(); System.exit(-1); } return MacAddr; }
public String getLocalIP() { String ip = ""; try { Enumeration<?> e1 = (Enumeration<?>) NetworkInterface .getNetworkInterfaces(); while (e1.hasMoreElements()) { NetworkInterface ni = (NetworkInterface) e1.nextElement(); if (!ni.getName().equals("eth0")) { continue; } else { Enumeration<?> e2 = ni.getInetAddresses(); while (e2.hasMoreElements()) { InetAddress ia = (InetAddress) e2.nextElement(); if (ia instanceof Inet6Address) continue; ip = ia.getHostAddress(); } break; } } } catch (SocketException e) { e.printStackTrace(); System.exit(-1); } return ip; }
/* 一个将字节转化为十六进制ASSIC码的函数 */ public static String byteHEX(byte ib) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; char[] ob = new char[2]; ob[0] = Digit[(ib >>> 4) & 0X0F]; ob[1] = Digit[ib & 0X0F]; String s = new String(ob); return s; }}